@snaha/swarm-id 0.0.1
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/README.md +431 -0
- package/dist/chunk/bmt.d.ts +17 -0
- package/dist/chunk/bmt.d.ts.map +1 -0
- package/dist/chunk/cac.d.ts +18 -0
- package/dist/chunk/cac.d.ts.map +1 -0
- package/dist/chunk/constants.d.ts +10 -0
- package/dist/chunk/constants.d.ts.map +1 -0
- package/dist/chunk/encrypted-cac.d.ts +48 -0
- package/dist/chunk/encrypted-cac.d.ts.map +1 -0
- package/dist/chunk/encryption.d.ts +86 -0
- package/dist/chunk/encryption.d.ts.map +1 -0
- package/dist/chunk/index.d.ts +6 -0
- package/dist/chunk/index.d.ts.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/proxy/act/act.d.ts +78 -0
- package/dist/proxy/act/act.d.ts.map +1 -0
- package/dist/proxy/act/crypto.d.ts +44 -0
- package/dist/proxy/act/crypto.d.ts.map +1 -0
- package/dist/proxy/act/grantee-list.d.ts +82 -0
- package/dist/proxy/act/grantee-list.d.ts.map +1 -0
- package/dist/proxy/act/history.d.ts +183 -0
- package/dist/proxy/act/history.d.ts.map +1 -0
- package/dist/proxy/act/index.d.ts +104 -0
- package/dist/proxy/act/index.d.ts.map +1 -0
- package/dist/proxy/chunking-encrypted.d.ts +14 -0
- package/dist/proxy/chunking-encrypted.d.ts.map +1 -0
- package/dist/proxy/chunking.d.ts +15 -0
- package/dist/proxy/chunking.d.ts.map +1 -0
- package/dist/proxy/download-data.d.ts +16 -0
- package/dist/proxy/download-data.d.ts.map +1 -0
- package/dist/proxy/feed-manifest.d.ts +62 -0
- package/dist/proxy/feed-manifest.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/async-finder.d.ts +77 -0
- package/dist/proxy/feeds/epochs/async-finder.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/epoch.d.ts +88 -0
- package/dist/proxy/feeds/epochs/epoch.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/finder.d.ts +67 -0
- package/dist/proxy/feeds/epochs/finder.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/index.d.ts +35 -0
- package/dist/proxy/feeds/epochs/index.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/test-utils.d.ts +93 -0
- package/dist/proxy/feeds/epochs/test-utils.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/types.d.ts +109 -0
- package/dist/proxy/feeds/epochs/types.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/updater.d.ts +68 -0
- package/dist/proxy/feeds/epochs/updater.d.ts.map +1 -0
- package/dist/proxy/feeds/epochs/utils.d.ts +22 -0
- package/dist/proxy/feeds/epochs/utils.d.ts.map +1 -0
- package/dist/proxy/feeds/index.d.ts +5 -0
- package/dist/proxy/feeds/index.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/async-finder.d.ts +14 -0
- package/dist/proxy/feeds/sequence/async-finder.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/finder.d.ts +17 -0
- package/dist/proxy/feeds/sequence/finder.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/index.d.ts +23 -0
- package/dist/proxy/feeds/sequence/index.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/types.d.ts +80 -0
- package/dist/proxy/feeds/sequence/types.d.ts.map +1 -0
- package/dist/proxy/feeds/sequence/updater.d.ts +26 -0
- package/dist/proxy/feeds/sequence/updater.d.ts.map +1 -0
- package/dist/proxy/index.d.ts +6 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/manifest-builder.d.ts +183 -0
- package/dist/proxy/manifest-builder.d.ts.map +1 -0
- package/dist/proxy/mantaray-encrypted.d.ts +27 -0
- package/dist/proxy/mantaray-encrypted.d.ts.map +1 -0
- package/dist/proxy/mantaray.d.ts +26 -0
- package/dist/proxy/mantaray.d.ts.map +1 -0
- package/dist/proxy/types.d.ts +29 -0
- package/dist/proxy/types.d.ts.map +1 -0
- package/dist/proxy/upload-data.d.ts +17 -0
- package/dist/proxy/upload-data.d.ts.map +1 -0
- package/dist/proxy/upload-encrypted-data.d.ts +103 -0
- package/dist/proxy/upload-encrypted-data.d.ts.map +1 -0
- package/dist/schemas.d.ts +240 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/storage/debounced-uploader.d.ts +62 -0
- package/dist/storage/debounced-uploader.d.ts.map +1 -0
- package/dist/storage/utilization-store.d.ts +108 -0
- package/dist/storage/utilization-store.d.ts.map +1 -0
- package/dist/swarm-id-auth.d.ts +74 -0
- package/dist/swarm-id-auth.d.ts.map +1 -0
- package/dist/swarm-id-auth.js +2 -0
- package/dist/swarm-id-auth.js.map +1 -0
- package/dist/swarm-id-client.d.ts +878 -0
- package/dist/swarm-id-client.d.ts.map +1 -0
- package/dist/swarm-id-client.js +2 -0
- package/dist/swarm-id-client.js.map +1 -0
- package/dist/swarm-id-proxy.d.ts +236 -0
- package/dist/swarm-id-proxy.d.ts.map +1 -0
- package/dist/swarm-id-proxy.js +2 -0
- package/dist/swarm-id-proxy.js.map +1 -0
- package/dist/swarm-id.esm.js +2 -0
- package/dist/swarm-id.esm.js.map +1 -0
- package/dist/swarm-id.umd.js +2 -0
- package/dist/swarm-id.umd.js.map +1 -0
- package/dist/sync/index.d.ts +9 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/key-derivation.d.ts +25 -0
- package/dist/sync/key-derivation.d.ts.map +1 -0
- package/dist/sync/restore-account.d.ts +28 -0
- package/dist/sync/restore-account.d.ts.map +1 -0
- package/dist/sync/serialization.d.ts +16 -0
- package/dist/sync/serialization.d.ts.map +1 -0
- package/dist/sync/store-interfaces.d.ts +53 -0
- package/dist/sync/store-interfaces.d.ts.map +1 -0
- package/dist/sync/sync-account.d.ts +44 -0
- package/dist/sync/sync-account.d.ts.map +1 -0
- package/dist/sync/types.d.ts +13 -0
- package/dist/sync/types.d.ts.map +1 -0
- package/dist/test-fixtures.d.ts +17 -0
- package/dist/test-fixtures.d.ts.map +1 -0
- package/dist/types-BD_VkNn0.js +2 -0
- package/dist/types-BD_VkNn0.js.map +1 -0
- package/dist/types-lJCaT-50.js +2 -0
- package/dist/types-lJCaT-50.js.map +1 -0
- package/dist/types.d.ts +2157 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/account-payload.d.ts +94 -0
- package/dist/utils/account-payload.d.ts.map +1 -0
- package/dist/utils/account-state-snapshot.d.ts +38 -0
- package/dist/utils/account-state-snapshot.d.ts.map +1 -0
- package/dist/utils/backup-encryption.d.ts +127 -0
- package/dist/utils/backup-encryption.d.ts.map +1 -0
- package/dist/utils/batch-utilization.d.ts +432 -0
- package/dist/utils/batch-utilization.d.ts.map +1 -0
- package/dist/utils/constants.d.ts +11 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/hex.d.ts +17 -0
- package/dist/utils/hex.d.ts.map +1 -0
- package/dist/utils/key-derivation.d.ts +92 -0
- package/dist/utils/key-derivation.d.ts.map +1 -0
- package/dist/utils/storage-managers.d.ts +65 -0
- package/dist/utils/storage-managers.d.ts.map +1 -0
- package/dist/utils/swarm-id-export.d.ts +24 -0
- package/dist/utils/swarm-id-export.d.ts.map +1 -0
- package/dist/utils/ttl.d.ts +49 -0
- package/dist/utils/ttl.d.ts.map +1 -0
- package/dist/utils/url.d.ts +41 -0
- package/dist/utils/url.d.ts.map +1 -0
- package/dist/utils/versioned-storage.d.ts +131 -0
- package/dist/utils/versioned-storage.d.ts.map +1 -0
- package/package.json +78 -0
- package/src/chunk/bmt.test.ts +217 -0
- package/src/chunk/bmt.ts +57 -0
- package/src/chunk/cac.test.ts +214 -0
- package/src/chunk/cac.ts +65 -0
- package/src/chunk/constants.ts +18 -0
- package/src/chunk/encrypted-cac.test.ts +385 -0
- package/src/chunk/encrypted-cac.ts +131 -0
- package/src/chunk/encryption.test.ts +352 -0
- package/src/chunk/encryption.ts +300 -0
- package/src/chunk/index.ts +47 -0
- package/src/index.ts +430 -0
- package/src/proxy/act/act.test.ts +278 -0
- package/src/proxy/act/act.ts +158 -0
- package/src/proxy/act/bee-compat.test.ts +948 -0
- package/src/proxy/act/crypto.test.ts +436 -0
- package/src/proxy/act/crypto.ts +376 -0
- package/src/proxy/act/grantee-list.test.ts +393 -0
- package/src/proxy/act/grantee-list.ts +239 -0
- package/src/proxy/act/history.test.ts +360 -0
- package/src/proxy/act/history.ts +413 -0
- package/src/proxy/act/index.test.ts +748 -0
- package/src/proxy/act/index.ts +853 -0
- package/src/proxy/chunking-encrypted.ts +95 -0
- package/src/proxy/chunking.ts +65 -0
- package/src/proxy/download-data.ts +448 -0
- package/src/proxy/feed-manifest.ts +174 -0
- package/src/proxy/feeds/epochs/async-finder.ts +372 -0
- package/src/proxy/feeds/epochs/epoch.test.ts +249 -0
- package/src/proxy/feeds/epochs/epoch.ts +181 -0
- package/src/proxy/feeds/epochs/finder.ts +282 -0
- package/src/proxy/feeds/epochs/index.ts +73 -0
- package/src/proxy/feeds/epochs/integration.test.ts +1336 -0
- package/src/proxy/feeds/epochs/test-utils.ts +274 -0
- package/src/proxy/feeds/epochs/types.ts +128 -0
- package/src/proxy/feeds/epochs/updater.ts +192 -0
- package/src/proxy/feeds/epochs/utils.ts +62 -0
- package/src/proxy/feeds/index.ts +5 -0
- package/src/proxy/feeds/sequence/async-finder.ts +31 -0
- package/src/proxy/feeds/sequence/finder.ts +73 -0
- package/src/proxy/feeds/sequence/index.ts +54 -0
- package/src/proxy/feeds/sequence/integration.test.ts +966 -0
- package/src/proxy/feeds/sequence/types.ts +103 -0
- package/src/proxy/feeds/sequence/updater.ts +71 -0
- package/src/proxy/index.ts +5 -0
- package/src/proxy/manifest-builder.test.ts +427 -0
- package/src/proxy/manifest-builder.ts +679 -0
- package/src/proxy/mantaray-encrypted.ts +78 -0
- package/src/proxy/mantaray.ts +104 -0
- package/src/proxy/types.ts +32 -0
- package/src/proxy/upload-data.ts +189 -0
- package/src/proxy/upload-encrypted-data.ts +658 -0
- package/src/schemas.ts +299 -0
- package/src/storage/debounced-uploader.ts +192 -0
- package/src/storage/utilization-store.ts +397 -0
- package/src/swarm-id-client.test.ts +99 -0
- package/src/swarm-id-client.ts +3095 -0
- package/src/swarm-id-proxy.ts +3891 -0
- package/src/sync/index.ts +28 -0
- package/src/sync/restore-account.ts +90 -0
- package/src/sync/serialization.ts +39 -0
- package/src/sync/store-interfaces.ts +62 -0
- package/src/sync/sync-account.test.ts +302 -0
- package/src/sync/sync-account.ts +396 -0
- package/src/sync/types.ts +11 -0
- package/src/test-fixtures.ts +109 -0
- package/src/types.ts +1651 -0
- package/src/utils/account-state-snapshot.test.ts +595 -0
- package/src/utils/account-state-snapshot.ts +94 -0
- package/src/utils/backup-encryption.test.ts +442 -0
- package/src/utils/backup-encryption.ts +352 -0
- package/src/utils/batch-utilization.ts +1309 -0
- package/src/utils/constants.ts +20 -0
- package/src/utils/hex.ts +27 -0
- package/src/utils/key-derivation.ts +197 -0
- package/src/utils/storage-managers.ts +365 -0
- package/src/utils/ttl.ts +129 -0
- package/src/utils/url.test.ts +136 -0
- package/src/utils/url.ts +71 -0
- package/src/utils/versioned-storage.ts +323 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for ACT History Management
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, vi } from "vitest"
|
|
6
|
+
import {
|
|
7
|
+
calculateReversedTimestamp,
|
|
8
|
+
calculateOriginalTimestamp,
|
|
9
|
+
createHistoryManifest,
|
|
10
|
+
addHistoryEntry,
|
|
11
|
+
getLatestEntry,
|
|
12
|
+
getEntryAtTimestamp,
|
|
13
|
+
getAllEntries,
|
|
14
|
+
serializeHistoryTree,
|
|
15
|
+
saveHistoryTreeRecursively,
|
|
16
|
+
deserializeHistory,
|
|
17
|
+
loadHistoryEntries,
|
|
18
|
+
getCurrentTimestamp,
|
|
19
|
+
} from "./history"
|
|
20
|
+
import { uint8ArrayToHex, hexToUint8Array } from "../../utils/hex"
|
|
21
|
+
|
|
22
|
+
// Helper to create a random 32-byte hex string
|
|
23
|
+
function randomHexRef(): string {
|
|
24
|
+
const bytes = new Uint8Array(32)
|
|
25
|
+
crypto.getRandomValues(bytes)
|
|
26
|
+
return uint8ArrayToHex(bytes)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("Timestamp calculations", () => {
|
|
30
|
+
it("calculateReversedTimestamp should reverse timestamp correctly", () => {
|
|
31
|
+
const timestamp = 1700000000
|
|
32
|
+
const reversed = calculateReversedTimestamp(timestamp)
|
|
33
|
+
|
|
34
|
+
// MaxInt64 = 9223372036854775807
|
|
35
|
+
// 9223372036854775807 - 1700000000 = 9223372035154775807
|
|
36
|
+
expect(reversed).toBe("9223372035154775807")
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it("calculateOriginalTimestamp should restore original timestamp", () => {
|
|
40
|
+
const original = 1700000000
|
|
41
|
+
const reversed = calculateReversedTimestamp(original)
|
|
42
|
+
const restored = calculateOriginalTimestamp(reversed)
|
|
43
|
+
|
|
44
|
+
expect(restored).toBe(original)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it("reversed timestamps should sort correctly (latest first)", () => {
|
|
48
|
+
const timestamps = [1000, 2000, 3000, 4000, 5000]
|
|
49
|
+
const reversed = timestamps.map(calculateReversedTimestamp)
|
|
50
|
+
|
|
51
|
+
// Sort ascending
|
|
52
|
+
reversed.sort((a, b) => a.localeCompare(b))
|
|
53
|
+
|
|
54
|
+
// After sorting, convert back to original timestamps
|
|
55
|
+
const sortedOriginals = reversed.map(calculateOriginalTimestamp)
|
|
56
|
+
|
|
57
|
+
// Should be sorted descending (latest first)
|
|
58
|
+
expect(sortedOriginals).toEqual([5000, 4000, 3000, 2000, 1000])
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("should handle edge cases for timestamps", () => {
|
|
62
|
+
// Very small timestamp
|
|
63
|
+
const small = calculateReversedTimestamp(0)
|
|
64
|
+
expect(calculateOriginalTimestamp(small)).toBe(0)
|
|
65
|
+
|
|
66
|
+
// Current time range timestamp
|
|
67
|
+
const now = Math.floor(Date.now() / 1000)
|
|
68
|
+
const nowReversed = calculateReversedTimestamp(now)
|
|
69
|
+
expect(calculateOriginalTimestamp(nowReversed)).toBe(now)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe("History manifest operations", () => {
|
|
74
|
+
it("createHistoryManifest should create empty MantarayNode", () => {
|
|
75
|
+
const manifest = createHistoryManifest()
|
|
76
|
+
expect(manifest).toBeDefined()
|
|
77
|
+
expect(manifest.forks.size).toBe(0)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it("addHistoryEntry should add entry with correct path and reference", () => {
|
|
81
|
+
const manifest = createHistoryManifest()
|
|
82
|
+
const timestamp = 1700000000
|
|
83
|
+
const actReference = randomHexRef()
|
|
84
|
+
|
|
85
|
+
addHistoryEntry(manifest, timestamp, actReference)
|
|
86
|
+
|
|
87
|
+
// Should have one fork
|
|
88
|
+
expect(manifest.forks.size).toBeGreaterThan(0)
|
|
89
|
+
|
|
90
|
+
// Get latest entry and verify
|
|
91
|
+
const latest = getLatestEntry(manifest)
|
|
92
|
+
expect(latest).toBeDefined()
|
|
93
|
+
expect(latest?.timestamp).toBe(timestamp)
|
|
94
|
+
expect(latest?.metadata.actReference).toBe(actReference)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it("addHistoryEntry should store encryptedGranteeListRef in metadata", () => {
|
|
98
|
+
const manifest = createHistoryManifest()
|
|
99
|
+
const timestamp = 1700000000
|
|
100
|
+
const actReference = randomHexRef()
|
|
101
|
+
const granteeListRef = randomHexRef()
|
|
102
|
+
|
|
103
|
+
addHistoryEntry(manifest, timestamp, actReference, granteeListRef)
|
|
104
|
+
|
|
105
|
+
const latest = getLatestEntry(manifest)
|
|
106
|
+
expect(latest).toBeDefined()
|
|
107
|
+
expect(latest?.metadata.encryptedGranteeListRef).toBe(granteeListRef)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it("getLatestEntry should return most recent entry", () => {
|
|
111
|
+
const manifest = createHistoryManifest()
|
|
112
|
+
|
|
113
|
+
// Add entries in order
|
|
114
|
+
const ref1 = randomHexRef()
|
|
115
|
+
const ref2 = randomHexRef()
|
|
116
|
+
const ref3 = randomHexRef()
|
|
117
|
+
|
|
118
|
+
addHistoryEntry(manifest, 1000, ref1)
|
|
119
|
+
addHistoryEntry(manifest, 2000, ref2)
|
|
120
|
+
addHistoryEntry(manifest, 3000, ref3)
|
|
121
|
+
|
|
122
|
+
const latest = getLatestEntry(manifest)
|
|
123
|
+
expect(latest).toBeDefined()
|
|
124
|
+
expect(latest?.timestamp).toBe(3000)
|
|
125
|
+
expect(latest?.metadata.actReference).toBe(ref3)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it("getLatestEntry should return undefined for empty manifest", () => {
|
|
129
|
+
const manifest = createHistoryManifest()
|
|
130
|
+
const latest = getLatestEntry(manifest)
|
|
131
|
+
expect(latest).toBeUndefined()
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it("getEntryAtTimestamp should find correct entry", () => {
|
|
135
|
+
const manifest = createHistoryManifest()
|
|
136
|
+
|
|
137
|
+
const ref1 = randomHexRef()
|
|
138
|
+
const ref2 = randomHexRef()
|
|
139
|
+
const ref3 = randomHexRef()
|
|
140
|
+
|
|
141
|
+
addHistoryEntry(manifest, 1000, ref1)
|
|
142
|
+
addHistoryEntry(manifest, 2000, ref2)
|
|
143
|
+
addHistoryEntry(manifest, 3000, ref3)
|
|
144
|
+
|
|
145
|
+
// Query exactly at timestamp 2000
|
|
146
|
+
const entry = getEntryAtTimestamp(manifest, 2000)
|
|
147
|
+
expect(entry).toBeDefined()
|
|
148
|
+
expect(entry?.timestamp).toBe(2000)
|
|
149
|
+
expect(entry?.metadata.actReference).toBe(ref2)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it("getEntryAtTimestamp should find entry at or before timestamp", () => {
|
|
153
|
+
const manifest = createHistoryManifest()
|
|
154
|
+
|
|
155
|
+
const ref1 = randomHexRef()
|
|
156
|
+
const ref2 = randomHexRef()
|
|
157
|
+
|
|
158
|
+
addHistoryEntry(manifest, 1000, ref1)
|
|
159
|
+
addHistoryEntry(manifest, 3000, ref2)
|
|
160
|
+
|
|
161
|
+
// Query at timestamp 2000 (between 1000 and 3000)
|
|
162
|
+
// Should return entry at 1000 (most recent at or before 2000)
|
|
163
|
+
const entry = getEntryAtTimestamp(manifest, 2000)
|
|
164
|
+
expect(entry).toBeDefined()
|
|
165
|
+
expect(entry?.timestamp).toBe(1000)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it("getAllEntries should return entries sorted newest first", () => {
|
|
169
|
+
const manifest = createHistoryManifest()
|
|
170
|
+
|
|
171
|
+
const ref1 = randomHexRef()
|
|
172
|
+
const ref2 = randomHexRef()
|
|
173
|
+
const ref3 = randomHexRef()
|
|
174
|
+
|
|
175
|
+
// Add in arbitrary order
|
|
176
|
+
addHistoryEntry(manifest, 2000, ref2)
|
|
177
|
+
addHistoryEntry(manifest, 1000, ref1)
|
|
178
|
+
addHistoryEntry(manifest, 3000, ref3)
|
|
179
|
+
|
|
180
|
+
const entries = getAllEntries(manifest)
|
|
181
|
+
|
|
182
|
+
expect(entries.length).toBe(3)
|
|
183
|
+
// Should be sorted newest first
|
|
184
|
+
expect(entries[0].timestamp).toBe(3000)
|
|
185
|
+
expect(entries[1].timestamp).toBe(2000)
|
|
186
|
+
expect(entries[2].timestamp).toBe(1000)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it("getAllEntries should return empty array for empty manifest", () => {
|
|
190
|
+
const manifest = createHistoryManifest()
|
|
191
|
+
const entries = getAllEntries(manifest)
|
|
192
|
+
expect(entries).toEqual([])
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
describe("History serialization", () => {
|
|
197
|
+
it("serializeHistoryTree should produce correct blobs", async () => {
|
|
198
|
+
const manifest = createHistoryManifest()
|
|
199
|
+
addHistoryEntry(manifest, 1000, randomHexRef())
|
|
200
|
+
|
|
201
|
+
const result = await serializeHistoryTree(manifest)
|
|
202
|
+
|
|
203
|
+
expect(result.blobs.size).toBeGreaterThan(0)
|
|
204
|
+
expect(result.rootReference).toBeDefined()
|
|
205
|
+
expect(result.rootReference.length).toBe(64) // 32 bytes hex
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it("saveHistoryTreeRecursively should upload bottom-up", async () => {
|
|
209
|
+
const manifest = createHistoryManifest()
|
|
210
|
+
addHistoryEntry(manifest, 1000, randomHexRef())
|
|
211
|
+
addHistoryEntry(manifest, 2000, randomHexRef())
|
|
212
|
+
|
|
213
|
+
const uploads: { data: Uint8Array; isRoot: boolean }[] = []
|
|
214
|
+
|
|
215
|
+
const uploadFn = vi.fn(async (data: Uint8Array, isRoot: boolean) => {
|
|
216
|
+
uploads.push({ data, isRoot })
|
|
217
|
+
// Return a mock reference based on content
|
|
218
|
+
const hash = new Uint8Array(32)
|
|
219
|
+
for (let i = 0; i < Math.min(data.length, 32); i++) {
|
|
220
|
+
hash[i] = data[i]
|
|
221
|
+
}
|
|
222
|
+
return { reference: uint8ArrayToHex(hash), tagUid: 123 }
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
const result = await saveHistoryTreeRecursively(manifest, uploadFn)
|
|
226
|
+
|
|
227
|
+
// Should have uploaded at least one node
|
|
228
|
+
expect(uploadFn).toHaveBeenCalled()
|
|
229
|
+
expect(result.rootReference).toBeDefined()
|
|
230
|
+
expect(result.tagUid).toBe(123)
|
|
231
|
+
|
|
232
|
+
// Last upload should be the root
|
|
233
|
+
expect(uploads[uploads.length - 1].isRoot).toBe(true)
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
it("deserializeHistory should parse MantarayNode format", async () => {
|
|
237
|
+
const manifest = createHistoryManifest()
|
|
238
|
+
const actRef = randomHexRef()
|
|
239
|
+
addHistoryEntry(manifest, 1000, actRef)
|
|
240
|
+
|
|
241
|
+
// Serialize
|
|
242
|
+
const serialized = await serializeHistoryTree(manifest)
|
|
243
|
+
|
|
244
|
+
// Deserialize root
|
|
245
|
+
const selfAddress = hexToUint8Array(serialized.rootReference)
|
|
246
|
+
const rootData = serialized.blobs.get(serialized.rootReference)
|
|
247
|
+
expect(rootData).toBeDefined()
|
|
248
|
+
|
|
249
|
+
const deserialized = deserializeHistory(rootData!, selfAddress)
|
|
250
|
+
expect(deserialized).toBeDefined()
|
|
251
|
+
expect(deserialized.forks.size).toBeGreaterThan(0)
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
it("loadHistoryEntries should load child nodes recursively", async () => {
|
|
255
|
+
const manifest = createHistoryManifest()
|
|
256
|
+
const actRef = randomHexRef()
|
|
257
|
+
const granteeListRef = randomHexRef()
|
|
258
|
+
addHistoryEntry(manifest, 1000, actRef, granteeListRef)
|
|
259
|
+
|
|
260
|
+
// Serialize
|
|
261
|
+
const serialized = await serializeHistoryTree(manifest)
|
|
262
|
+
|
|
263
|
+
// Deserialize root
|
|
264
|
+
const selfAddress = hexToUint8Array(serialized.rootReference)
|
|
265
|
+
const rootData = serialized.blobs.get(serialized.rootReference)!
|
|
266
|
+
const deserialized = deserializeHistory(rootData, selfAddress)
|
|
267
|
+
|
|
268
|
+
// Load children
|
|
269
|
+
await loadHistoryEntries(deserialized, async (ref) => {
|
|
270
|
+
const data = serialized.blobs.get(ref)
|
|
271
|
+
if (!data) {
|
|
272
|
+
throw new Error(`Data not found for reference: ${ref}`)
|
|
273
|
+
}
|
|
274
|
+
return data
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
// Now should be able to get entries
|
|
278
|
+
const entry = getLatestEntry(deserialized)
|
|
279
|
+
expect(entry).toBeDefined()
|
|
280
|
+
expect(entry?.timestamp).toBe(1000)
|
|
281
|
+
expect(entry?.metadata.actReference).toBe(actRef)
|
|
282
|
+
expect(entry?.metadata.encryptedGranteeListRef).toBe(granteeListRef)
|
|
283
|
+
})
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
describe("getCurrentTimestamp", () => {
|
|
287
|
+
it("should return current Unix timestamp in seconds", () => {
|
|
288
|
+
const before = Math.floor(Date.now() / 1000)
|
|
289
|
+
const timestamp = getCurrentTimestamp()
|
|
290
|
+
const after = Math.floor(Date.now() / 1000)
|
|
291
|
+
|
|
292
|
+
expect(timestamp).toBeGreaterThanOrEqual(before)
|
|
293
|
+
expect(timestamp).toBeLessThanOrEqual(after)
|
|
294
|
+
})
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
describe("History roundtrip", () => {
|
|
298
|
+
it("should preserve all data through serialize/deserialize/load cycle", async () => {
|
|
299
|
+
const manifest = createHistoryManifest()
|
|
300
|
+
|
|
301
|
+
// Add multiple entries with metadata
|
|
302
|
+
const entries = [
|
|
303
|
+
{
|
|
304
|
+
timestamp: 1000,
|
|
305
|
+
actRef: randomHexRef(),
|
|
306
|
+
granteeListRef: randomHexRef(),
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
timestamp: 2000,
|
|
310
|
+
actRef: randomHexRef(),
|
|
311
|
+
granteeListRef: randomHexRef(),
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
timestamp: 3000,
|
|
315
|
+
actRef: randomHexRef(),
|
|
316
|
+
granteeListRef: randomHexRef(),
|
|
317
|
+
},
|
|
318
|
+
]
|
|
319
|
+
|
|
320
|
+
for (const e of entries) {
|
|
321
|
+
addHistoryEntry(manifest, e.timestamp, e.actRef, e.granteeListRef)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Serialize
|
|
325
|
+
const serialized = await serializeHistoryTree(manifest)
|
|
326
|
+
|
|
327
|
+
// Deserialize
|
|
328
|
+
const selfAddress = hexToUint8Array(serialized.rootReference)
|
|
329
|
+
const rootData = serialized.blobs.get(serialized.rootReference)!
|
|
330
|
+
const deserialized = deserializeHistory(rootData, selfAddress)
|
|
331
|
+
|
|
332
|
+
// Load children
|
|
333
|
+
await loadHistoryEntries(deserialized, async (ref) => {
|
|
334
|
+
const data = serialized.blobs.get(ref)
|
|
335
|
+
if (!data) {
|
|
336
|
+
throw new Error(`Data not found for reference: ${ref}`)
|
|
337
|
+
}
|
|
338
|
+
return data
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
// Verify all entries are present
|
|
342
|
+
const allEntries = getAllEntries(deserialized)
|
|
343
|
+
expect(allEntries.length).toBe(3)
|
|
344
|
+
|
|
345
|
+
// Verify entries are sorted newest first
|
|
346
|
+
expect(allEntries[0].timestamp).toBe(3000)
|
|
347
|
+
expect(allEntries[1].timestamp).toBe(2000)
|
|
348
|
+
expect(allEntries[2].timestamp).toBe(1000)
|
|
349
|
+
|
|
350
|
+
// Verify metadata is preserved
|
|
351
|
+
for (const original of entries) {
|
|
352
|
+
const found = allEntries.find((e) => e.timestamp === original.timestamp)
|
|
353
|
+
expect(found).toBeDefined()
|
|
354
|
+
expect(found?.metadata.actReference).toBe(original.actRef)
|
|
355
|
+
expect(found?.metadata.encryptedGranteeListRef).toBe(
|
|
356
|
+
original.granteeListRef,
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
})
|
|
360
|
+
})
|