0eb25286c8
Deux mouvements de surface, aucun changement de comportement. **`packages/client` → `packages/sdk`, `@ng-eventually/client` → `@ng-eventually/sdk`.** « client » ne disait rien : ce paquet EST le SDK que l'application appelle, et c'est tout ce qu'elle appelle. L'ancien nom reste comme mot-clé de recherche dans `docs/source-layout-by-fate.md` et le tableau des paquets du README. **Une seule entrée.** L'entrée `./polyfill` disparaît ; ses symboles applicatifs — `configure`, `configureStoreRegistry`, `setCurrentUser`, `connectedUser` et leurs types — vivent dans un bloc `POLYFILL-ERA` de `src/index.ts`. Ce que la seconde porte portait mérite d'être nommé avant d'être retiré : *ce qu'on importe de ce chemin est exactement ce qu'on supprimera à la migration*. Une seule porte perd ce signal — rien à la ligne d'import ne distingue `configure`, qui part, de `docs`, que le vrai SDK remplace sur place. Trois choses le portent désormais : le bloc lui-même, l'inventaire d'exports de `docs/api-contract.md` (épinglé par `test/vocabulary.test.ts`, donc il ne peut pas rancir en silence), et le contrôle de vocabulaire sur les noms publiés. **Six symboles quittent la surface au passage**, et la fusion est ce qui a rendu le choix visible plutôt qu'hérité : - `getConfig` / `getStoreRegistryDeps` — câblage interne, atteint par `shared-wallet/bootstrap` ; - `resetConfig` / `resetStoreRegistry` / `resetCaps` — remises à zéro de test, atteintes par leur chemin interne, ce qui est leur raison d'être ; - le `share` direct — `inbox.share` a toujours été la même fonction, et la publier deux fois brouillait la frontière qu'elle servait à marquer. Corrections d'affirmations fausses trouvées en chemin : le contrat annonçait `isNuri` / `hasReadCap` sur la porte SDK alors qu'ils ne sont plus exportés depuis le passage au permissif en entrée (`NuriLike` validé à la porte) ; le README du paquet documentait `capFor`, `shareCap`, `getCaps` et `publishRepoLink`, dont aucun n'existe ; et le README de l'app d'exemple affirmait que la suite e2e la pilote, ce qui reste à faire. 179 tests unitaires, typecheck bibliothèque / exemple / harnais, e2e 42/42 contre le broker en ligne — mesuré une fois après le renommage, une fois après la fusion.
100 lines
4.0 KiB
TypeScript
100 lines
4.0 KiB
TypeScript
import { test, expect, mock, beforeEach } from "bun:test";
|
|
import { docCreate, sparqlUpdate, sparqlQuery } from "../src/surface/docs";
|
|
|
|
// The reach guard is process-wide: once ANY cap exists it applies to every reader.
|
|
// This suite declares none, so it must not inherit another suite's enforcement.
|
|
beforeEach(() => {
|
|
resetCaps();
|
|
setCurrentUser(null);
|
|
});
|
|
import * as ngProxy from "../src/surface/ng-proxy";
|
|
|
|
// NOTE ORDER: the "not configured → throw" case MUST run before any configure()
|
|
// call, because configure() sets a module-level singleton with no public reset.
|
|
|
|
test("throws a clear error when configure() was not called", async () => {
|
|
await expect(docCreate("sid", "Graph", "data:graph", "store")).rejects.toThrow(
|
|
/configure\(\) must be called before use/,
|
|
);
|
|
await expect(sparqlUpdate("sid", "INSERT DATA {}")).rejects.toThrow(
|
|
/configure\(\) must be called before use/,
|
|
);
|
|
await expect(sparqlQuery("sid", "SELECT * {}")).rejects.toThrow(
|
|
/configure\(\) must be called before use/,
|
|
);
|
|
});
|
|
|
|
// From here on, a fake real `ng` is injected via configure().
|
|
import { configure, setCurrentUser } from "../src/index";
|
|
import { resetCaps } from "../src/shared-wallet/bootstrap";
|
|
|
|
function fakeNg() {
|
|
return {
|
|
doc_create: mock(async (..._a: unknown[]) => "did:ng:o:new-doc"),
|
|
sparql_update: mock(async (..._a: unknown[]) => undefined),
|
|
sparql_query: mock(async (..._a: unknown[]) => ({ results: { bindings: [] } })),
|
|
// A sentinel: makeNg(), if ever used, would `.bind` and call THIS through
|
|
// the JS Proxy. We assert the primitives call the raw fns above directly.
|
|
};
|
|
}
|
|
|
|
function inject() {
|
|
const ng = fakeNg();
|
|
configure({ ng: ng as any, useShape: (() => {}) as any });
|
|
return ng;
|
|
}
|
|
|
|
test("docCreate calls the real injected ng.doc_create with the exact args", async () => {
|
|
const ng = inject();
|
|
const nuri = await docCreate("sid-1", "Graph", "data:graph", "store", undefined);
|
|
expect(nuri).toBe("did:ng:o:new-doc");
|
|
expect(ng.doc_create).toHaveBeenCalledTimes(1);
|
|
expect(ng.doc_create.mock.calls[0]).toEqual(["sid-1", "Graph", "data:graph", "store", undefined]);
|
|
});
|
|
|
|
test("sparqlUpdate forwards (sessionId, query, anchor) to the real ng.sparql_update", async () => {
|
|
const ng = inject();
|
|
await sparqlUpdate("sid-2", "INSERT DATA { GRAPH <did:ng:o:a> { <s> <p> <o> } }", "did:ng:o:a");
|
|
expect(ng.sparql_update).toHaveBeenCalledTimes(1);
|
|
expect(ng.sparql_update.mock.calls[0]).toEqual([
|
|
"sid-2",
|
|
"INSERT DATA { GRAPH <did:ng:o:a> { <s> <p> <o> } }",
|
|
"did:ng:o:a",
|
|
]);
|
|
});
|
|
|
|
test("sparqlUpdate passes anchor=undefined when omitted", async () => {
|
|
const ng = inject();
|
|
await sparqlUpdate("sid-3", "INSERT DATA {}");
|
|
expect(ng.sparql_update.mock.calls[0]).toEqual(["sid-3", "INSERT DATA {}", undefined]);
|
|
});
|
|
|
|
test("sparqlQuery forwards (sessionId, query, base, anchor) and returns the raw result", async () => {
|
|
const ng = inject();
|
|
const res = await sparqlQuery("sid-4", "SELECT ?e { GRAPH <g> { ?s ?p ?e } }", undefined, "did:ng:o:g");
|
|
expect(res).toEqual({ results: { bindings: [] } });
|
|
expect(ng.sparql_query).toHaveBeenCalledTimes(1);
|
|
expect(ng.sparql_query.mock.calls[0]).toEqual([
|
|
"sid-4",
|
|
"SELECT ?e { GRAPH <g> { ?s ?p ?e } }",
|
|
undefined,
|
|
"did:ng:o:g",
|
|
]);
|
|
});
|
|
|
|
test("the primitives do NOT route through the public ng proxy (makeNg)", async () => {
|
|
// makeNg builds a JS Proxy over the injected ng. If a primitive went through
|
|
// it, calls would land on the proxy's `get` trap, not on our raw mock fns.
|
|
// Spy on makeNg: it must never be invoked by the docs primitives.
|
|
const spy = mock(ngProxy.makeNg);
|
|
const ng = inject();
|
|
await docCreate("sid", "Graph", "data:graph", "store");
|
|
await sparqlUpdate("sid", "INSERT DATA {}");
|
|
await sparqlQuery("sid", "SELECT * {}");
|
|
expect(spy).toHaveBeenCalledTimes(0);
|
|
// And the raw injected fns were reached directly:
|
|
expect(ng.doc_create).toHaveBeenCalledTimes(1);
|
|
expect(ng.sparql_update).toHaveBeenCalledTimes(1);
|
|
expect(ng.sparql_query).toHaveBeenCalledTimes(1);
|
|
});
|