Q121: a file id keeps the bytes it was recorded with; rewrites get new ids
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
04c8cce0c4
commit
8f192eabe7
11 files changed
+143
-19
No files matched your search
@@ -24,7 +24,7 @@ describe("domain services", () => {
|
||||
const connection = createConnection();
|
||||
|
||||
const rows = connection.db.prepare("select version from schema_migrations order by version asc").all() as Array<{ version: number }>;
|
||||
expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88]);
|
||||
expect(rows.map((row) => row.version)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89]);
|
||||
connection.close();
|
||||
});
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ function agent(id: string, department: string): AgentRecord {
|
||||
|
||||
function file(id: string, ownerType: FileObjectRecord["ownerType"], ownerId: string | null): FileObjectRecord {
|
||||
return {
|
||||
id, orgId: "org", name: id, kind: "doc", sizeBytes: 1, sha256: "x", storagePath: `/tmp/${id}`,
|
||||
id, orgId: "org", name: id, kind: "doc", sizeBytes: 1, sha256: "x", storagePath: `/tmp/${id}`, sourcePath: null, supersededAt: null,
|
||||
ownerType, ownerId, spaceId: null, pinned: false, scanState: "approved", scanLabelsJson: "[]",
|
||||
scannedAt: null, createdAt: "2026-01-01", updatedAt: null,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { createHash, randomUUID } from "node:crypto";
|
||||
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { DatabaseConnection } from "../../src/main/database/connection";
|
||||
import { OrganizationService } from "../../src/main/services/organization-service";
|
||||
import { preserveRecordedFiles, recordWorkspaceFile } from "../../src/main/services/files/file-object-store";
|
||||
|
||||
describe("recorded workspace files", () => {
|
||||
test("a file id keeps the bytes it was recorded with after the workspace file is rewritten", () => {
|
||||
const connection = new DatabaseConnection(join(tmpdir(), `ac-versions-${randomUUID()}.db`));
|
||||
const root = join(tmpdir(), `ac-versions-ws-${randomUUID()}`);
|
||||
mkdirSync(root, { recursive: true });
|
||||
let orgId = "";
|
||||
connection.transaction(() => {
|
||||
orgId = new OrganizationService(connection.db).createOrganization({ organizationName: "Acme", employeeName: "Dana", mission: "m", goal: "g" });
|
||||
});
|
||||
const record = () => recordWorkspaceFile(connection.db, { orgId, ownerType: "agent", ownerId: null, workspaceRoot: root, relativePath: "plan.md" });
|
||||
const file = (id: string) => connection.db.prepare("select storage_path, superseded_at from file_objects where id = ?").get(id) as { storage_path: string; superseded_at: string | null };
|
||||
|
||||
writeFileSync(join(root, "plan.md"), "handed over");
|
||||
const first = record();
|
||||
expect(record()).toBe(first);
|
||||
writeFileSync(join(root, "plan.md"), "rewritten later");
|
||||
const second = record();
|
||||
|
||||
expect(second).not.toBe(first);
|
||||
expect(readFileSync(file(first).storage_path, "utf8")).toBe("handed over");
|
||||
expect(readFileSync(file(second).storage_path, "utf8")).toBe("rewritten later");
|
||||
expect(file(first).superseded_at).not.toBeNull();
|
||||
expect(file(second).superseded_at).toBeNull();
|
||||
connection.close();
|
||||
});
|
||||
|
||||
test("ids recorded before copies existed get their bytes kept at startup, with their extension", () => {
|
||||
const connection = new DatabaseConnection(join(tmpdir(), `ac-legacy-${randomUUID()}.db`));
|
||||
const root = join(tmpdir(), `ac-legacy-ws-${randomUUID()}`);
|
||||
mkdirSync(root, { recursive: true });
|
||||
let orgId = "";
|
||||
connection.transaction(() => {
|
||||
orgId = new OrganizationService(connection.db).createOrganization({ organizationName: "Acme", employeeName: "Dana", mission: "m", goal: "g" });
|
||||
new OrganizationService(connection.db).createWorkspace({ orgId, name: "HQ", localPath: root, projectId: null });
|
||||
});
|
||||
const live = join(root, "report.md");
|
||||
writeFileSync(live, "as handed over");
|
||||
const sha = createHash("sha256").update("as handed over").digest("hex");
|
||||
const now = new Date().toISOString();
|
||||
connection.db.prepare(`
|
||||
insert into file_objects(id, org_id, name, kind, size_bytes, sha256, storage_path, source_path, owner_type, owner_id, pinned, scan_state, scan_labels_json, created_at)
|
||||
values ('fil_legacy', ?, 'report.md', 'doc', 14, ?, ?, ?, 'agent', null, 0, 'approved', '[]', ?)
|
||||
`).run(orgId, sha, live, live, now);
|
||||
preserveRecordedFiles(connection.db);
|
||||
writeFileSync(live, "rewritten");
|
||||
const kept = (connection.db.prepare("select storage_path from file_objects where id = 'fil_legacy'").get() as { storage_path: string }).storage_path;
|
||||
expect(kept.endsWith(`${sha}.md`)).toBe(true);
|
||||
expect(readFileSync(kept, "utf8")).toBe("as handed over");
|
||||
connection.close();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user