@nkmc/gateway 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-56RA53VS.js +37 -0
- package/dist/chunk-CZJ75YTV.js +969 -0
- package/dist/chunk-QGM4M3NI.js +37 -0
- package/dist/http.cjs +1772 -0
- package/dist/http.d.cts +49 -0
- package/dist/http.d.ts +49 -0
- package/dist/http.js +748 -0
- package/dist/index.cjs +2436 -0
- package/dist/index.d.cts +436 -0
- package/dist/index.d.ts +436 -0
- package/dist/index.js +1434 -0
- package/dist/proxy-ClPcDgsO.d.cts +283 -0
- package/dist/proxy-qpda1ANS.d.ts +283 -0
- package/dist/proxy.cjs +148 -0
- package/dist/proxy.d.cts +6 -0
- package/dist/proxy.d.ts +6 -0
- package/dist/proxy.js +90 -0
- package/dist/testing.cjs +865 -0
- package/dist/testing.d.cts +12 -0
- package/dist/testing.d.ts +12 -0
- package/dist/testing.js +831 -0
- package/dist/tunnels-BviBEaih.d.cts +12 -0
- package/dist/tunnels-DFHNgmN7.d.ts +12 -0
- package/dist/types-C6JC9oTm.d.cts +21 -0
- package/dist/types-C6JC9oTm.d.ts +21 -0
- package/package.json +47 -0
- package/src/__tests__/sqlite-integration.test.ts +384 -0
- package/src/credential/d1-vault.ts +134 -0
- package/src/credential/memory-vault.ts +50 -0
- package/src/credential/types.ts +16 -0
- package/src/d1/__tests__/sqlite-adapter.test.ts +75 -0
- package/src/d1/sqlite-adapter.ts +59 -0
- package/src/d1/types.ts +22 -0
- package/src/federation/__tests__/d1-peer-store.test.ts +218 -0
- package/src/federation/__tests__/peer-client.test.ts +205 -0
- package/src/federation/__tests__/peer-store.test.ts +114 -0
- package/src/federation/d1-peer-store.ts +164 -0
- package/src/federation/peer-backend.ts +60 -0
- package/src/federation/peer-client.ts +122 -0
- package/src/federation/peer-store.ts +45 -0
- package/src/federation/types.ts +39 -0
- package/src/http/app.ts +152 -0
- package/src/http/lib/dns.ts +30 -0
- package/src/http/middleware/admin-auth.ts +18 -0
- package/src/http/middleware/agent-auth.ts +27 -0
- package/src/http/middleware/publish-auth.ts +39 -0
- package/src/http/routes/__tests__/federation.test.ts +364 -0
- package/src/http/routes/__tests__/peers.test.ts +290 -0
- package/src/http/routes/__tests__/proxy.test.ts +159 -0
- package/src/http/routes/auth.ts +39 -0
- package/src/http/routes/byok.ts +62 -0
- package/src/http/routes/credentials.ts +40 -0
- package/src/http/routes/domains.ts +174 -0
- package/src/http/routes/federation.ts +170 -0
- package/src/http/routes/fs.ts +89 -0
- package/src/http/routes/peers.ts +103 -0
- package/src/http/routes/proxy.ts +57 -0
- package/src/http/routes/registry.ts +222 -0
- package/src/http/routes/tunnels.ts +124 -0
- package/src/http.ts +9 -0
- package/src/index.ts +63 -0
- package/src/metering/d1-store.ts +123 -0
- package/src/metering/memory-store.ts +29 -0
- package/src/metering/pricing-guard.ts +68 -0
- package/src/metering/types.ts +25 -0
- package/src/onboard/apis-guru.ts +64 -0
- package/src/onboard/index.ts +4 -0
- package/src/onboard/manifest.ts +362 -0
- package/src/onboard/pipeline.ts +214 -0
- package/src/onboard/types.ts +72 -0
- package/src/proxy/__tests__/tool-registry.test.ts +93 -0
- package/src/proxy/tool-registry.ts +122 -0
- package/src/proxy.ts +12 -0
- package/src/registry/context7-backend.ts +93 -0
- package/src/registry/context7.ts +54 -0
- package/src/registry/d1-store.ts +242 -0
- package/src/registry/memory-store.ts +101 -0
- package/src/registry/openapi-compiler.ts +284 -0
- package/src/registry/resolver.ts +196 -0
- package/src/registry/rpc-compiler.ts +142 -0
- package/src/registry/skill-parser.ts +119 -0
- package/src/registry/skill-to-config.ts +239 -0
- package/src/registry/source-refresher.ts +83 -0
- package/src/registry/types.ts +129 -0
- package/src/registry/virtual-files.ts +76 -0
- package/src/testing/sqlite-d1.ts +64 -0
- package/src/testing.ts +2 -0
- package/src/tunnel/__tests__/cloudflare-provider.test.ts +255 -0
- package/src/tunnel/__tests__/tunnel.test.ts +542 -0
- package/src/tunnel/cloudflare-provider.ts +121 -0
- package/src/tunnel/memory-store.ts +30 -0
- package/src/tunnel/types.ts +28 -0
- package/test/credential/d1-vault.test.ts +127 -0
- package/test/credential/injection.test.ts +67 -0
- package/test/credential/memory-vault.test.ts +63 -0
- package/test/http/app.test.ts +300 -0
- package/test/http/byok-e2e.test.ts +240 -0
- package/test/http/byok.test.ts +115 -0
- package/test/http/credentials.test.ts +57 -0
- package/test/http/e2e.test.ts +260 -0
- package/test/integration/authenticated-apis.test.ts +185 -0
- package/test/integration/free-apis-e2e.test.ts +222 -0
- package/test/metering/d1-store.test.ts +82 -0
- package/test/metering/memory-store.test.ts +76 -0
- package/test/metering/pricing-guard.test.ts +108 -0
- package/test/onboard/apis-guru.test.ts +57 -0
- package/test/onboard/e2e.test.ts +70 -0
- package/test/onboard/pipeline.test.ts +318 -0
- package/test/onboard/real-apis.test.ts +483 -0
- package/test/registry/compilation-correctness.test.ts +132 -0
- package/test/registry/context7-backend.test.ts +88 -0
- package/test/registry/context7-e2e.test.ts +92 -0
- package/test/registry/context7.test.ts +73 -0
- package/test/registry/d1-store.test.ts +184 -0
- package/test/registry/integration.test.ts +129 -0
- package/test/registry/lazy-mount.test.ts +138 -0
- package/test/registry/memory-store.test.ts +171 -0
- package/test/registry/openapi-compiler.test.ts +267 -0
- package/test/registry/openapi-e2e.test.ts +154 -0
- package/test/registry/passthrough-e2e.test.ts +109 -0
- package/test/registry/resolver-peer.test.ts +299 -0
- package/test/registry/resolver.test.ts +228 -0
- package/test/registry/rpc-compiler.test.ts +112 -0
- package/test/registry/skill-parser.test.ts +151 -0
- package/test/registry/skill-to-config.test.ts +151 -0
- package/test/registry/skill-to-rpc-config.test.ts +142 -0
- package/test/registry/source-refresher.test.ts +90 -0
- package/test/registry/virtual-files.test.ts +96 -0
- package/tsconfig.json +4 -0
- package/tsup.config.ts +8 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { skillToRpcConfig } from "../../src/registry/skill-to-config.js";
|
|
3
|
+
import type { RpcSourceMeta } from "../../src/registry/types.js";
|
|
4
|
+
|
|
5
|
+
const EVM_META: RpcSourceMeta = {
|
|
6
|
+
rpcUrl: "https://rpc.ankr.com/eth",
|
|
7
|
+
convention: "evm",
|
|
8
|
+
resources: [
|
|
9
|
+
{
|
|
10
|
+
name: "blocks",
|
|
11
|
+
methods: { list: "eth_blockNumber", read: "eth_getBlockByNumber" },
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: "balances",
|
|
15
|
+
methods: { read: "eth_getBalance" },
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const CRUD_META: RpcSourceMeta = {
|
|
21
|
+
rpcUrl: "https://example.com/rpc",
|
|
22
|
+
convention: "crud",
|
|
23
|
+
resources: [
|
|
24
|
+
{
|
|
25
|
+
name: "users",
|
|
26
|
+
idField: "id",
|
|
27
|
+
methods: {
|
|
28
|
+
list: "user.list",
|
|
29
|
+
read: "user.get",
|
|
30
|
+
create: "user.create",
|
|
31
|
+
write: "user.update",
|
|
32
|
+
remove: "user.delete",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const RAW_META: RpcSourceMeta = {
|
|
39
|
+
rpcUrl: "https://example.com/rpc",
|
|
40
|
+
convention: "raw",
|
|
41
|
+
resources: [
|
|
42
|
+
{
|
|
43
|
+
name: "info",
|
|
44
|
+
methods: { read: "getInfo" },
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
describe("skillToRpcConfig", () => {
|
|
50
|
+
it("should return resources matching meta input", () => {
|
|
51
|
+
const { resources } = skillToRpcConfig(EVM_META);
|
|
52
|
+
expect(resources).toHaveLength(2);
|
|
53
|
+
expect(resources[0].name).toBe("blocks");
|
|
54
|
+
expect(resources[1].name).toBe("balances");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should rebuild RPC methods with params callbacks", () => {
|
|
58
|
+
const { resources } = skillToRpcConfig(EVM_META);
|
|
59
|
+
const blocks = resources[0];
|
|
60
|
+
expect(blocks.methods.list).toBeDefined();
|
|
61
|
+
expect(blocks.methods.read).toBeDefined();
|
|
62
|
+
expect(blocks.methods.list!.method).toBe("eth_blockNumber");
|
|
63
|
+
expect(blocks.methods.read!.method).toBe("eth_getBlockByNumber");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should generate evm params for read (hex encoding)", () => {
|
|
67
|
+
const { resources } = skillToRpcConfig(EVM_META);
|
|
68
|
+
const blocks = resources[0];
|
|
69
|
+
// numeric id → hex
|
|
70
|
+
const params = blocks.methods.read!.params({ id: "100" });
|
|
71
|
+
expect(params).toEqual(["0x64", "latest"]);
|
|
72
|
+
// hex id stays as-is
|
|
73
|
+
const params2 = blocks.methods.read!.params({ id: "0xabc" });
|
|
74
|
+
expect(params2).toEqual(["0xabc", "latest"]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should generate evm params for list (no params for eth_blockNumber)", () => {
|
|
78
|
+
const { resources } = skillToRpcConfig(EVM_META);
|
|
79
|
+
const blocks = resources[0];
|
|
80
|
+
const params = blocks.methods.list!.params({});
|
|
81
|
+
expect(params).toEqual([]);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("should add evm transforms for blocks (hex block number → recent block list)", () => {
|
|
85
|
+
const { resources } = skillToRpcConfig(EVM_META);
|
|
86
|
+
const blocks = resources[0];
|
|
87
|
+
expect(blocks.transform).toBeDefined();
|
|
88
|
+
expect(blocks.transform!.list).toBeDefined();
|
|
89
|
+
|
|
90
|
+
// eth_blockNumber returns a hex string → transform to last 10 block .json files
|
|
91
|
+
const result = blocks.transform!.list!("0xa") as string[];
|
|
92
|
+
expect(result).toHaveLength(10);
|
|
93
|
+
expect(result[0]).toBe("10.json");
|
|
94
|
+
expect(result[9]).toBe("1.json");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("should add evm transforms for balances", () => {
|
|
98
|
+
const { resources } = skillToRpcConfig(EVM_META);
|
|
99
|
+
const balances = resources[1];
|
|
100
|
+
expect(balances.transform).toBeDefined();
|
|
101
|
+
expect(balances.transform!.read).toBeDefined();
|
|
102
|
+
|
|
103
|
+
const result = balances.transform!.read!("0xde0b6b3a7640000");
|
|
104
|
+
expect(result).toEqual({ wei: "0xde0b6b3a7640000", raw: "0xde0b6b3a7640000" });
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("should generate crud params correctly", () => {
|
|
108
|
+
const { resources } = skillToRpcConfig(CRUD_META);
|
|
109
|
+
const users = resources[0];
|
|
110
|
+
|
|
111
|
+
expect(users.methods.list!.params({})).toEqual([]);
|
|
112
|
+
expect(users.methods.read!.params({ id: "123" })).toEqual(["123"]);
|
|
113
|
+
expect(users.methods.create!.params({ data: { name: "test" } })).toEqual([{ name: "test" }]);
|
|
114
|
+
expect(users.methods.write!.params({ id: "123", data: { name: "updated" } })).toEqual(["123", { name: "updated" }]);
|
|
115
|
+
expect(users.methods.remove!.params({ id: "123" })).toEqual(["123"]);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("should preserve idField from meta", () => {
|
|
119
|
+
const { resources } = skillToRpcConfig(CRUD_META);
|
|
120
|
+
expect(resources[0].idField).toBe("id");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should handle raw convention with pass-through params", () => {
|
|
124
|
+
const { resources } = skillToRpcConfig(RAW_META);
|
|
125
|
+
const info = resources[0];
|
|
126
|
+
expect(info.methods.read!.method).toBe("getInfo");
|
|
127
|
+
// With id
|
|
128
|
+
expect(info.methods.read!.params({ id: "abc" })).toEqual(["abc"]);
|
|
129
|
+
// With data
|
|
130
|
+
expect(info.methods.read!.params({ data: { x: 1 } })).toEqual([{ x: 1 }]);
|
|
131
|
+
// Empty
|
|
132
|
+
expect(info.methods.read!.params({})).toEqual([]);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should not add transforms for non-evm conventions", () => {
|
|
136
|
+
const { resources } = skillToRpcConfig(CRUD_META);
|
|
137
|
+
expect(resources[0].transform).toBeUndefined();
|
|
138
|
+
|
|
139
|
+
const { resources: rawResources } = skillToRpcConfig(RAW_META);
|
|
140
|
+
expect(rawResources[0].transform).toBeUndefined();
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { SourceRefresher } from "../../src/registry/source-refresher.js";
|
|
3
|
+
import { MemoryRegistryStore } from "../../src/registry/memory-store.js";
|
|
4
|
+
import type { ServiceRecord } from "../../src/registry/types.js";
|
|
5
|
+
|
|
6
|
+
function makeRecord(domain: string, overrides?: Partial<ServiceRecord>): ServiceRecord {
|
|
7
|
+
return {
|
|
8
|
+
domain,
|
|
9
|
+
name: overrides?.name ?? domain,
|
|
10
|
+
description: overrides?.description ?? `Service ${domain}`,
|
|
11
|
+
version: overrides?.version ?? "1.0",
|
|
12
|
+
roles: ["agent"],
|
|
13
|
+
skillMd: "---\nname: test\n---\n# Test\n\nTest service.\n",
|
|
14
|
+
endpoints: [],
|
|
15
|
+
isFirstParty: false,
|
|
16
|
+
createdAt: Date.now(),
|
|
17
|
+
updatedAt: Date.now(),
|
|
18
|
+
status: "active",
|
|
19
|
+
isDefault: true,
|
|
20
|
+
...overrides,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe("SourceRefresher", () => {
|
|
25
|
+
let store: MemoryRegistryStore;
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
store = new MemoryRegistryStore();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should not refresh without source config", async () => {
|
|
32
|
+
const record = makeRecord("test.com");
|
|
33
|
+
const refresher = new SourceRefresher(store);
|
|
34
|
+
expect(await refresher.shouldRefresh(record)).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should not refresh when interval has not elapsed", async () => {
|
|
38
|
+
const record = makeRecord("test.com", {
|
|
39
|
+
source: { type: "openapi", url: "https://test.com/spec.json", refreshInterval: 3600, lastRefresh: Date.now() },
|
|
40
|
+
});
|
|
41
|
+
const refresher = new SourceRefresher(store);
|
|
42
|
+
expect(await refresher.shouldRefresh(record)).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should refresh when interval has elapsed", async () => {
|
|
46
|
+
const record = makeRecord("test.com", {
|
|
47
|
+
source: { type: "openapi", url: "https://test.com/spec.json", refreshInterval: 1, lastRefresh: Date.now() - 2000 },
|
|
48
|
+
});
|
|
49
|
+
const refresher = new SourceRefresher(store);
|
|
50
|
+
expect(await refresher.shouldRefresh(record)).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should refresh openapi source", async () => {
|
|
54
|
+
const spec = { info: { title: "Updated", version: "2.0" }, paths: {} };
|
|
55
|
+
const mockFetch = async () => new Response(JSON.stringify(spec), { status: 200, headers: { "Content-Type": "application/json" } });
|
|
56
|
+
|
|
57
|
+
const record = makeRecord("test.com", {
|
|
58
|
+
source: { type: "openapi", url: "https://test.com/spec.json", refreshInterval: 1 },
|
|
59
|
+
});
|
|
60
|
+
await store.put("test.com", record);
|
|
61
|
+
|
|
62
|
+
const refresher = new SourceRefresher(store, mockFetch as any);
|
|
63
|
+
const updated = await refresher.refresh(record);
|
|
64
|
+
expect(updated?.name).toBe("Updated");
|
|
65
|
+
expect(updated?.source?.lastRefresh).toBeGreaterThan(0);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should refresh wellknown source", async () => {
|
|
69
|
+
const skillMd = '---\nname: "Refreshed"\nversion: "2.0"\nroles: [agent]\n---\n\n# Refreshed\n\nRefreshed service.\n';
|
|
70
|
+
const mockFetch = async () => new Response(skillMd, { status: 200 });
|
|
71
|
+
|
|
72
|
+
const record = makeRecord("test.com", {
|
|
73
|
+
source: { type: "wellknown", url: "https://test.com/.well-known/skill.md", refreshInterval: 1 },
|
|
74
|
+
});
|
|
75
|
+
await store.put("test.com", record);
|
|
76
|
+
|
|
77
|
+
const refresher = new SourceRefresher(store, mockFetch as any);
|
|
78
|
+
const updated = await refresher.refresh(record);
|
|
79
|
+
expect(updated?.name).toBe("Refreshed");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should return null for unknown source type", async () => {
|
|
83
|
+
const record = makeRecord("test.com", {
|
|
84
|
+
source: { type: "skillmd" },
|
|
85
|
+
});
|
|
86
|
+
const refresher = new SourceRefresher(store);
|
|
87
|
+
const result = await refresher.refresh(record);
|
|
88
|
+
expect(result).toBeNull();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { VirtualFileBackend } from "../../src/registry/virtual-files.js";
|
|
3
|
+
import { MemoryRegistryStore } from "../../src/registry/memory-store.js";
|
|
4
|
+
import { parseSkillMd } from "../../src/registry/skill-parser.js";
|
|
5
|
+
import type { FsBackend } from "@nkmc/agent-fs";
|
|
6
|
+
|
|
7
|
+
const SKILL_MD = `---
|
|
8
|
+
name: "Test API"
|
|
9
|
+
gateway: nkmc
|
|
10
|
+
version: "1.0"
|
|
11
|
+
roles: [agent]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Test API
|
|
15
|
+
|
|
16
|
+
A test service.
|
|
17
|
+
|
|
18
|
+
## API
|
|
19
|
+
|
|
20
|
+
### List items
|
|
21
|
+
|
|
22
|
+
\`GET /api/items\` — free
|
|
23
|
+
|
|
24
|
+
### Create order
|
|
25
|
+
|
|
26
|
+
\`POST /api/orders\` — 0.05 USDC / call
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
class MockBackend implements FsBackend {
|
|
30
|
+
async list(_path: string): Promise<string[]> { return ["items/", "_api/"]; }
|
|
31
|
+
async read(_path: string): Promise<unknown> { return { mock: true }; }
|
|
32
|
+
async write(_path: string, _data: unknown): Promise<{id: string}> { return { id: "1" }; }
|
|
33
|
+
async remove(_path: string): Promise<void> {}
|
|
34
|
+
async search(_path: string, _pattern: string): Promise<unknown[]> { return []; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe("VirtualFileBackend", () => {
|
|
38
|
+
let store: MemoryRegistryStore;
|
|
39
|
+
let inner: MockBackend;
|
|
40
|
+
let backend: VirtualFileBackend;
|
|
41
|
+
|
|
42
|
+
beforeEach(async () => {
|
|
43
|
+
store = new MemoryRegistryStore();
|
|
44
|
+
const record = parseSkillMd("test-api.com", SKILL_MD);
|
|
45
|
+
await store.put("test-api.com", record);
|
|
46
|
+
|
|
47
|
+
inner = new MockBackend();
|
|
48
|
+
backend = new VirtualFileBackend({ inner, domain: "test-api.com", store });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should append virtual files to root listing", async () => {
|
|
52
|
+
const entries = await backend.list("/");
|
|
53
|
+
expect(entries).toContain("items/");
|
|
54
|
+
expect(entries).toContain("_api/");
|
|
55
|
+
expect(entries).toContain("_pricing.json");
|
|
56
|
+
expect(entries).toContain("_versions.json");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should not append virtual files to non-root listing", async () => {
|
|
60
|
+
const entries = await backend.list("/items");
|
|
61
|
+
expect(entries).not.toContain("_pricing.json");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should read _pricing.json", async () => {
|
|
65
|
+
const data = await backend.read("/_pricing.json") as any;
|
|
66
|
+
expect(data.domain).toBe("test-api.com");
|
|
67
|
+
expect(data.endpoints.length).toBeGreaterThan(0);
|
|
68
|
+
expect(data.endpoints[0].pricing).toBeDefined();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should read _versions.json", async () => {
|
|
72
|
+
const data = await backend.read("/_versions.json") as any;
|
|
73
|
+
expect(data.domain).toBe("test-api.com");
|
|
74
|
+
expect(data.versions).toHaveLength(1);
|
|
75
|
+
expect(data.versions[0].version).toBe("1.0");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should delegate non-virtual reads to inner backend", async () => {
|
|
79
|
+
const data = await backend.read("/items/1.json");
|
|
80
|
+
expect(data).toEqual({ mock: true });
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should delegate write to inner backend", async () => {
|
|
84
|
+
const result = await backend.write("/items/", { name: "test" });
|
|
85
|
+
expect(result.id).toBe("1");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should delegate remove to inner backend", async () => {
|
|
89
|
+
await expect(backend.remove("/items/1")).resolves.toBeUndefined();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should delegate search to inner backend", async () => {
|
|
93
|
+
const results = await backend.search("/items", "test");
|
|
94
|
+
expect(results).toEqual([]);
|
|
95
|
+
});
|
|
96
|
+
});
|
package/tsconfig.json
ADDED