Files
suaveplan/packages/networking/drive/drive-sharing/tests/integration.test.ts
2026-05-27 23:47:53 +01:00

137 lines
4.5 KiB
TypeScript

import { describe, expect, it } from "@suaveplan/testing/runner";
import {
fileId,
GrantManager,
InMemoryPartitionStore,
KeyRing,
PartitionManager,
TokenManager,
TransferManager,
userId,
wrapKey,
} from "../src/index.js";
import { getDefaultPolicy } from "../src/partitions/partition-policy/partition-policy.js";
describe("drive-sharing — end-to-end integration", () => {
it("create partition, add member, grant access, transfer file", {
timeout: 30000,
}, async () => {
// Build the full lifecycle stack with deterministic clocks / IDs.
const events = new Map<string, unknown[]>();
const bus = {
on: () => () => undefined,
emit: (k: string, p: unknown) => {
const arr = events.get(k) ?? [];
arr.push(p);
events.set(k, arr);
},
};
let counter = 0;
const idGen = () => `id-${(counter += 1)}`;
const masterKey = new Uint8Array(32).fill(0x77);
const keyRing = new KeyRing({ masterKey });
const store = new InMemoryPartitionStore();
const partitions = new PartitionManager({
store,
keyRing,
clock: () => 1_000,
idGenerator: idGen,
events: bus,
});
const grants = new GrantManager({
clock: () => 1_000,
idGenerator: idGen,
events: bus,
});
const tokens = new TokenManager({
clock: () => 1_000,
defaultTTL: 60_000,
masterKey,
idGenerator: idGen,
});
// 1. Create source and target partitions.
const src = await partitions.create({
name: "research",
type: "shared",
ownerId: userId("alice"),
});
const dst = await partitions.create({
name: "review",
type: "shared",
ownerId: userId("alice"),
});
// 2. Add bob as a member of source.
const updatedSrc = await partitions.addMember(src.id, userId("bob"), {
permissions: ["read", "share"],
});
expect(updatedSrc.members.length).toBe(1);
// 3. Mint a grant for bob to read the file.
const grant = grants.create({
grantorId: userId("alice"),
granteeId: userId("bob"),
partitionId: src.id,
permissions: ["read"],
expiresAt: 5_000_000,
});
const check = grants.check(grant.id, userId("bob"), "read");
expect(check.allowed).toBe(true);
// 4. Mint a share token for the file.
const token = tokens.create({
partitionId: src.id,
createdBy: userId("alice"),
permissions: ["read"],
});
expect(tokens.validate(token.id).valid).toBe(true);
// 5. Set up transfer with a real wrapped DEK.
const sourceKek = new Uint8Array(32).fill(0xaa);
const targetKek = new Uint8Array(32).fill(0xbb);
const dek = new Uint8Array(32).fill(0xcc);
const aadSrc = new TextEncoder().encode(
`drive-sharing/v1/transfer/${src.id}/${fileId("f-1")}`
);
const wrapped = await wrapKey(sourceKek, dek, { aad: aadSrc });
const transfer = new TransferManager({
kekResolver: async (_uid, pid) =>
pid === src.id ? sourceKek : targetKek,
policyResolver: async (pid) => {
const partition =
pid === src.id ? updatedSrc : { ...dst, members: [] };
return {
policy: getDefaultPolicy(),
partition,
};
},
wrappedKeyResolver: async () => wrapped,
clock: () => 2_000,
});
const result = await transfer.transfer({
fileId: fileId("f-1"),
sourcePartitionId: src.id,
targetPartitionId: dst.id,
userId: userId("alice"),
direction: "export",
});
expect(result.success).toBe(true);
expect(result.rewrappedKey).toBeDefined();
// 6. Verify lifecycle event emission.
expect(events.get("partition:created")?.length).toBe(2);
expect(events.get("member:added")?.length).toBe(1);
expect(events.get("grant:created")?.length).toBe(1);
// 7. Revoke the grant and confirm subsequent checks are denied.
grants.revoke(grant.id);
expect(grants.check(grant.id, userId("bob"), "read").allowed).toBe(
false
);
});
});