📄 src/models/test/draft.test.ts
D-OPEN SOVEREIGN
1/*
2 * Copyright (c) 2026 DataPond D-Library Pty Ltd. ("The Sanctuary")
3 * Non-Profit Public Library — The World Library
4 * 
5 * D-SAFE Certification issued by POND Enterprise.
6 * Published under the D-Open Code Sovereign Licence v1.0 framework
7 * DataPond D-Library All exclusive Right reserved on modifiying the code - CC Attribution to data pond, Non modifiable, Non Commercial.
8 * https://registry.world.bibliotech.com/licence
9 * Source code donated to DataPond D-Library by it's author: data pond.
10 * 
11 * Technical Guardian ("The Shield"): Pond Enterprise Pty Ltd. (ACN 694 747 987)
12 * All liability claims about D-SAFE certification issues coming from the published D-Safe direction must be addressed with Pond Enterprise Pty Ltd.
13 * Code Modification automatically voids the certified liability protection provided by Pond Enterprise.
14 */
15import { describe, it, expect, beforeEach } from "vitest";
16import { Book, NewBookWithId } from "../generated-src/Book.ts";
17import { clear, configureDb } from "../db.ts";
18import { setDefaultLanguage } from "../orm.ts";
19import { PatchInstance } from "../patcher.ts";
20import {
21    createDraft,
22    isDraft,
23    isDraftDirty,
24    commitDraft,
25    rollbackDraft,
26    getDraftChanges,
27    getDraftTarget
28} from "../draft.ts";
29
30beforeEach(() => {
31    clear();
32    configureDb({ cacheLimit: 10_000 });
33    setDefaultLanguage("eng");
34    PatchInstance.clearQueue();
35});
36
37describe("Draft Proxy Integration", () => {
38    it("recognizes draft proxy using helper functions", () => {
39        const book = NewBookWithId(101);
40        const draft = createDraft(book);
41
42        expect(isDraft(book)).toBe(false);
43        expect(isDraft(draft)).toBe(true);
44        expect(getDraftTarget(draft)).toBe(book);
45    });
46
47    it("reads fall back to original model and writes are buffered", () => {
48        const book = NewBookWithId(102);
49        book.setTitle("Original Title", "eng");
50        book.description = "Original Description";
51
52        const draft = createDraft(book);
53
54        // Reads fall back
55        expect(draft.title).toBe("Original Title");
56        expect(draft.description).toBe("Original Description");
57        expect(isDraftDirty(draft)).toBe(false);
58
59        // Writes do not mutate original model, only draft
60        draft.description = "New Description";
61        expect(isDraftDirty(draft)).toBe(true);
62        expect(draft.description).toBe("New Description");
63        expect(book.description).toBe("Original Description");
64
65        // Map has changes
66        const changes = getDraftChanges(draft);
67        expect(changes).toBeDefined();
68        expect(changes?.get("description")).toBe("New Description");
69    });
70
71    it("commitDraft applies changes to model and triggers patches", () => {
72        const book = NewBookWithId(103);
73        book.description = "Original";
74        PatchInstance.clearQueue();
75
76        const draft = createDraft(book);
77        draft.description = "Committed Description";
78
79        expect(book.description).toBe("Original");
80        expect(PatchInstance.patches.length).toBe(0);
81
82        commitDraft(draft);
83
84        // Changes are now applied
85        expect(book.description).toBe("Committed Description");
86        expect(draft.description).toBe("Committed Description");
87        expect(isDraftDirty(draft)).toBe(false);
88
89        // Patches are enqueued
90        expect(PatchInstance.patches.length).toBe(1);
91        expect((PatchInstance.patches[0] as any).value).toBe("Committed Description");
92    });
93
94    it("rollbackDraft discards buffered changes", () => {
95        const book = NewBookWithId(104);
96        book.description = "Original";
97
98        const draft = createDraft(book);
99        draft.description = "Temp Edit";
100        expect(draft.description).toBe("Temp Edit");
101        expect(isDraftDirty(draft)).toBe(true);
102
103        rollbackDraft(draft);
104
105        expect(draft.description).toBe("Original");
106        expect(book.description).toBe("Original");
107        expect(isDraftDirty(draft)).toBe(false);
108    });
109
110    it("pins the method-bypass limitation (methods read target state, not draft buffer)", () => {
111        const book = NewBookWithId(105);
112        book.setTitle("Original Title", "eng");
113
114        const draft = createDraft(book);
115        draft.title = "Draft Title";
116
117        // Property access reads from the draft buffer
118        expect(draft.title).toBe("Draft Title");
119
120        // Localized method access binds to target and bypasses proxy buffer
121        expect(draft.getTitle("eng")).toBe("Original Title");
122    });
123
124    it("warns on console and fails to mutate target for non-settable/typo properties", () => {
125        const book = NewBookWithId(106);
126        const draft = createDraft(book);
127
128        // Attempting to write a non-settable or typo property buffers it in the draft Map
129        (draft as any).typoField = "Oops";
130        expect(isDraftDirty(draft)).toBe(true);
131
132        const warnings: string[] = [];
133        const originalWarn = console.warn;
134        console.warn = (msg: string) => {
135            warnings.push(msg);
136        };
137
138        try {
139            commitDraft(draft);
140        } finally {
141            console.warn = originalWarn;
142        }
143
144        // Committing typoField fails on Reflect.set and logs a warning
145        expect(warnings.length).toBe(1);
146        expect(warnings[0]).toContain('Failed to commit property "typoField"');
147        expect((book as any).typoField).toBeUndefined();
148    });
149
150    it("short-circuits and does not mark draft dirty when setting value to current value", () => {
151        const book = NewBookWithId(107);
152        book.description = "Original";
153
154        const draft = createDraft(book);
155        
156        // Setting to current value has no effect on dirty status
157        draft.description = "Original";
158        expect(isDraftDirty(draft)).toBe(false);
159
160        // Mutating makes it dirty
161        draft.description = "New Description";
162        expect(isDraftDirty(draft)).toBe(true);
163
164        // Restoring to current value cleans it up
165        draft.description = "Original";
166        expect(isDraftDirty(draft)).toBe(false);
167    });
168});
169