@xyo-network/diviner-image-thumbnail 4.2.0 → 5.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/dist/neutral/Diviner/ImageThumbnailDivinerState.d.ts +2 -2
- package/dist/neutral/Diviner/ImageThumbnailDivinerState.d.ts.map +1 -1
- package/package.json +39 -36
- package/src/Diviner/ImageThumbnailDivinerState.ts +2 -2
- package/src/Diviner/ImageThumbnailStateToIndexCandidateDiviner/spec/Diviner.spec.ts +175 -0
- package/src/Diviner/spec/Diviner.spec.ts +261 -0
- package/src/Diviner/spec/ImageThumbnailIndexCandidateToImageThumbnailIndexDiviner.spec.ts +98 -0
- package/src/Diviner/spec/ImageThumbnailIndexQueryResponseToImageThumbnailQueryResponseDiviner.spec.ts +120 -0
- package/src/Diviner/spec/ImageThumbnailQueryToImageThumbnailIndexQueryDiviner.spec.ts +145 -0
- package/typedoc.json +0 -5
- package/xy.config.ts +0 -10
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Hex } from '@xylabs/hex';
|
|
2
1
|
import type { StateDictionary } from '@xyo-network/module-model';
|
|
2
|
+
import type { Sequence } from '@xyo-network/payload-model';
|
|
3
3
|
export type ImageThumbnailDivinerState = StateDictionary & {
|
|
4
|
-
cursor:
|
|
4
|
+
cursor: Sequence;
|
|
5
5
|
};
|
|
6
6
|
//# sourceMappingURL=ImageThumbnailDivinerState.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageThumbnailDivinerState.d.ts","sourceRoot":"","sources":["../../../src/Diviner/ImageThumbnailDivinerState.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"ImageThumbnailDivinerState.d.ts","sourceRoot":"","sources":["../../../src/Diviner/ImageThumbnailDivinerState.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAE1D,MAAM,MAAM,0BAA0B,GAAG,eAAe,GAAG;IACzD,MAAM,EAAE,QAAQ,CAAA;CACjB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xyo-network/diviner-image-thumbnail",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "Typescript/Javascript Plugins for XYO Platform",
|
|
5
5
|
"homepage": "https://xyo.network",
|
|
6
6
|
"bugs": {
|
|
@@ -28,44 +28,47 @@
|
|
|
28
28
|
},
|
|
29
29
|
"module": "dist/neutral/index.mjs",
|
|
30
30
|
"types": "dist/neutral/index.d.ts",
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"src"
|
|
34
|
+
],
|
|
31
35
|
"dependencies": {
|
|
32
|
-
"@xylabs/array": "
|
|
33
|
-
"@xylabs/assert": "
|
|
34
|
-
"@xylabs/exists": "
|
|
35
|
-
"@xylabs/
|
|
36
|
-
"@
|
|
37
|
-
"@xyo-network/archivist-
|
|
38
|
-
"@xyo-network/
|
|
39
|
-
"@xyo-network/boundwitness-
|
|
40
|
-
"@xyo-network/
|
|
41
|
-
"@xyo-network/diviner-
|
|
42
|
-
"@xyo-network/diviner-
|
|
43
|
-
"@xyo-network/diviner-model": "
|
|
44
|
-
"@xyo-network/diviner-
|
|
45
|
-
"@xyo-network/
|
|
46
|
-
"@xyo-network/
|
|
47
|
-
"@xyo-network/
|
|
48
|
-
"@xyo-network/payload-
|
|
49
|
-
"@xyo-network/payload-
|
|
50
|
-
"@xyo-network/
|
|
51
|
-
"@xyo-network/witness-timestamp": "^4.3.0"
|
|
36
|
+
"@xylabs/array": "~5.0.7",
|
|
37
|
+
"@xylabs/assert": "~5.0.7",
|
|
38
|
+
"@xylabs/exists": "~5.0.7",
|
|
39
|
+
"@xylabs/object": "~5.0.7",
|
|
40
|
+
"@xyo-network/archivist-model": "~5.0.2",
|
|
41
|
+
"@xyo-network/archivist-wrapper": "~5.0.2",
|
|
42
|
+
"@xyo-network/boundwitness-model": "~5.0.2",
|
|
43
|
+
"@xyo-network/boundwitness-validator": "~5.0.2",
|
|
44
|
+
"@xyo-network/diviner-abstract": "~5.0.2",
|
|
45
|
+
"@xyo-network/diviner-indexing": "~5.0.2",
|
|
46
|
+
"@xyo-network/diviner-model": "~5.0.2",
|
|
47
|
+
"@xyo-network/diviner-payload-model": "~5.0.2",
|
|
48
|
+
"@xyo-network/diviner-wrapper": "~5.0.2",
|
|
49
|
+
"@xyo-network/image-thumbnail-payload-plugin": "~5.0.1",
|
|
50
|
+
"@xyo-network/module-model": "~5.0.2",
|
|
51
|
+
"@xyo-network/payload-builder": "~5.0.2",
|
|
52
|
+
"@xyo-network/payload-model": "~5.0.2",
|
|
53
|
+
"@xyo-network/url-payload-plugin": "~5.0.1",
|
|
54
|
+
"@xyo-network/witness-timestamp": "~5.0.2"
|
|
52
55
|
},
|
|
53
56
|
"devDependencies": {
|
|
54
|
-
"@xylabs/delay": "
|
|
55
|
-
"@xylabs/hex": "
|
|
56
|
-
"@xylabs/ts-scripts-yarn3": "
|
|
57
|
-
"@xylabs/tsconfig": "
|
|
58
|
-
"@xylabs/vitest-extended": "
|
|
59
|
-
"@xyo-network/archivist-memory": "
|
|
60
|
-
"@xyo-network/boundwitness-builder": "
|
|
61
|
-
"@xyo-network/diviner-boundwitness-memory": "
|
|
62
|
-
"@xyo-network/diviner-payload-generic": "
|
|
63
|
-
"@xyo-network/manifest": "
|
|
64
|
-
"@xyo-network/module-factory-locator": "
|
|
65
|
-
"@xyo-network/node-memory": "
|
|
66
|
-
"@xyo-network/wallet": "
|
|
67
|
-
"typescript": "
|
|
68
|
-
"vitest": "
|
|
57
|
+
"@xylabs/delay": "~5.0.7",
|
|
58
|
+
"@xylabs/hex": "~5.0.7",
|
|
59
|
+
"@xylabs/ts-scripts-yarn3": "~7.1.0",
|
|
60
|
+
"@xylabs/tsconfig": "~7.1.0",
|
|
61
|
+
"@xylabs/vitest-extended": "~5.0.7",
|
|
62
|
+
"@xyo-network/archivist-memory": "~5.0.2",
|
|
63
|
+
"@xyo-network/boundwitness-builder": "~5.0.2",
|
|
64
|
+
"@xyo-network/diviner-boundwitness-memory": "~5.0.2",
|
|
65
|
+
"@xyo-network/diviner-payload-generic": "~5.0.2",
|
|
66
|
+
"@xyo-network/manifest": "~5.0.2",
|
|
67
|
+
"@xyo-network/module-factory-locator": "~5.0.2",
|
|
68
|
+
"@xyo-network/node-memory": "~5.0.2",
|
|
69
|
+
"@xyo-network/wallet": "~5.0.2",
|
|
70
|
+
"typescript": "~5.9.2",
|
|
71
|
+
"vitest": "~3.2.4"
|
|
69
72
|
},
|
|
70
73
|
"publishConfig": {
|
|
71
74
|
"access": "public"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Hex } from '@xylabs/hex'
|
|
2
1
|
import type { StateDictionary } from '@xyo-network/module-model'
|
|
2
|
+
import type { Sequence } from '@xyo-network/payload-model'
|
|
3
3
|
|
|
4
4
|
export type ImageThumbnailDivinerState = StateDictionary & {
|
|
5
|
-
cursor:
|
|
5
|
+
cursor: Sequence
|
|
6
6
|
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import { filterAs } from '@xylabs/array'
|
|
4
|
+
import { assertEx } from '@xylabs/assert'
|
|
5
|
+
import { delay } from '@xylabs/delay'
|
|
6
|
+
import { MemoryArchivist } from '@xyo-network/archivist-memory'
|
|
7
|
+
import { asArchivistInstance } from '@xyo-network/archivist-model'
|
|
8
|
+
import { BoundWitnessBuilder } from '@xyo-network/boundwitness-builder'
|
|
9
|
+
import { isBoundWitness } from '@xyo-network/boundwitness-model'
|
|
10
|
+
import { MemoryBoundWitnessDiviner } from '@xyo-network/diviner-boundwitness-memory'
|
|
11
|
+
import { asDivinerInstance } from '@xyo-network/diviner-model'
|
|
12
|
+
import { GenericPayloadDiviner } from '@xyo-network/diviner-payload-generic'
|
|
13
|
+
import type { ImageThumbnail } from '@xyo-network/image-thumbnail-payload-plugin'
|
|
14
|
+
import { isImageThumbnail } from '@xyo-network/image-thumbnail-payload-plugin'
|
|
15
|
+
import type { PackageManifestPayload } from '@xyo-network/manifest'
|
|
16
|
+
import { ManifestWrapper } from '@xyo-network/manifest'
|
|
17
|
+
import { ModuleFactoryLocator } from '@xyo-network/module-factory-locator'
|
|
18
|
+
import type { ModuleState } from '@xyo-network/module-model'
|
|
19
|
+
import { isModuleState, ModuleStateSchema } from '@xyo-network/module-model'
|
|
20
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
21
|
+
import type { Payload, WithStorageMeta } from '@xyo-network/payload-model'
|
|
22
|
+
import { asOptionalStorageMeta } from '@xyo-network/payload-model'
|
|
23
|
+
import { HDWallet } from '@xyo-network/wallet'
|
|
24
|
+
import type { TimeStamp } from '@xyo-network/witness-timestamp'
|
|
25
|
+
import { isTimestamp, TimestampSchema } from '@xyo-network/witness-timestamp'
|
|
26
|
+
import {
|
|
27
|
+
beforeAll,
|
|
28
|
+
describe, expect,
|
|
29
|
+
it,
|
|
30
|
+
} from 'vitest'
|
|
31
|
+
|
|
32
|
+
import type { ImageThumbnailDivinerState } from '../../ImageThumbnailDivinerState.ts'
|
|
33
|
+
import { ImageThumbnailStateToIndexCandidateDiviner } from '../Diviner.ts'
|
|
34
|
+
import ImageThumbnailStateToIndexCandidateDivinerManifest from './ImageThumbnailStateToIndexCandidateDiviner.json' with { type: 'json' }
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @group slow
|
|
38
|
+
*/
|
|
39
|
+
describe('ImageThumbnailStateToIndexCandidateDiviner', () => {
|
|
40
|
+
const sourceUrl = 'https://placekitten.com/200/300'
|
|
41
|
+
const thumbnailHttpSuccess: ImageThumbnail = {
|
|
42
|
+
http: { status: 200 },
|
|
43
|
+
schema: 'network.xyo.image.thumbnail',
|
|
44
|
+
sourceHash: '7f39363514d9d9b958a5a993edeba35cb44f912c7072ed9ddd628728ac0fd681',
|
|
45
|
+
sourceUrl,
|
|
46
|
+
url: 'data:image/png;base64,===',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const thumbnailHttpFail: ImageThumbnail = {
|
|
50
|
+
http: {
|
|
51
|
+
ipAddress: '104.17.96.13',
|
|
52
|
+
status: 429,
|
|
53
|
+
},
|
|
54
|
+
schema: 'network.xyo.image.thumbnail',
|
|
55
|
+
sourceUrl,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const thumbnailCodeFail: ImageThumbnail = {
|
|
59
|
+
http: { code: 'FAILED' },
|
|
60
|
+
schema: 'network.xyo.image.thumbnail',
|
|
61
|
+
sourceUrl,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const thumbnailWitnessFail: ImageThumbnail = {
|
|
65
|
+
http: { ipAddress: '104.17.96.13' },
|
|
66
|
+
schema: 'network.xyo.image.thumbnail',
|
|
67
|
+
sourceUrl,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let testCases: WithStorageMeta<Payload>[][] = []
|
|
71
|
+
let archivist: MemoryArchivist
|
|
72
|
+
let sut: ImageThumbnailStateToIndexCandidateDiviner
|
|
73
|
+
|
|
74
|
+
beforeAll(async () => {
|
|
75
|
+
const wallet = await HDWallet.random()
|
|
76
|
+
const locator = new ModuleFactoryLocator()
|
|
77
|
+
locator.register(MemoryArchivist.factory())
|
|
78
|
+
locator.register(MemoryBoundWitnessDiviner.factory())
|
|
79
|
+
locator.register(GenericPayloadDiviner.factory())
|
|
80
|
+
locator.register(ImageThumbnailStateToIndexCandidateDiviner.factory())
|
|
81
|
+
const manifest = ImageThumbnailStateToIndexCandidateDivinerManifest as PackageManifestPayload
|
|
82
|
+
const manifestWrapper = new ManifestWrapper(manifest, wallet, locator)
|
|
83
|
+
const node = await manifestWrapper.loadNodeFromIndex(0)
|
|
84
|
+
await node.start()
|
|
85
|
+
|
|
86
|
+
const privateModules = manifest.nodes[0].modules?.private ?? []
|
|
87
|
+
const publicModules = manifest.nodes[0].modules?.public ?? []
|
|
88
|
+
const mods = await node.resolve('*')
|
|
89
|
+
expect(mods.length).toBe(privateModules.length + publicModules.length + 1)
|
|
90
|
+
|
|
91
|
+
// Insert previously witnessed payloads into thumbnail archivist
|
|
92
|
+
const httpSuccessTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: 1 }
|
|
93
|
+
const [httpSuccessBoundWitness, httpSuccessPayloads] = await new BoundWitnessBuilder()
|
|
94
|
+
.payloads([thumbnailHttpSuccess, httpSuccessTimestamp])
|
|
95
|
+
.build()
|
|
96
|
+
|
|
97
|
+
const httpFailTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: 2 }
|
|
98
|
+
const [httpFailBoundWitness, httpFailPayloads] = await new BoundWitnessBuilder().payloads([thumbnailHttpFail, httpFailTimestamp]).build()
|
|
99
|
+
|
|
100
|
+
const witnessFailTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: 3 }
|
|
101
|
+
const [witnessFailBoundWitness, witnessFailPayloads] = await new BoundWitnessBuilder()
|
|
102
|
+
.payloads([thumbnailWitnessFail, witnessFailTimestamp])
|
|
103
|
+
.build()
|
|
104
|
+
|
|
105
|
+
const codeFailTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: 4 }
|
|
106
|
+
const [codeFailBoundWitness, codeFailPayloads] = await new BoundWitnessBuilder().payloads([thumbnailCodeFail, codeFailTimestamp]).build()
|
|
107
|
+
|
|
108
|
+
archivist = assertEx(asArchivistInstance<MemoryArchivist>(await node.resolve('ImageThumbnailArchivist')))
|
|
109
|
+
const testCasesToCreate = [
|
|
110
|
+
[httpSuccessBoundWitness, ...httpSuccessPayloads],
|
|
111
|
+
[httpFailBoundWitness, ...httpFailPayloads],
|
|
112
|
+
[witnessFailBoundWitness, ...witnessFailPayloads],
|
|
113
|
+
[codeFailBoundWitness, ...codeFailPayloads],
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
for (const [bw, ...payloads] of testCasesToCreate) {
|
|
117
|
+
const createdTestCase = []
|
|
118
|
+
for (const payload of [bw, ...payloads]) {
|
|
119
|
+
await delay(2)
|
|
120
|
+
const [signedPayload] = await archivist.insert([payload])
|
|
121
|
+
createdTestCase.push(signedPayload)
|
|
122
|
+
}
|
|
123
|
+
testCases.push(createdTestCase)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
sut = assertEx(asDivinerInstance(await node.resolve('ImageThumbnailStateToIndexCandidateDiviner'))) as ImageThumbnailStateToIndexCandidateDiviner
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
describe('divine', () => {
|
|
130
|
+
describe('with no previous state', () => {
|
|
131
|
+
it('returns next state and batch results', async () => {
|
|
132
|
+
const results = await sut.divine()
|
|
133
|
+
expect(results.length).toBe(testCases.flat().length + 1)
|
|
134
|
+
const state = results.find(isModuleState<ImageThumbnailDivinerState>) as WithStorageMeta<ModuleState<ImageThumbnailDivinerState>>
|
|
135
|
+
expect(state).toBeDefined()
|
|
136
|
+
const last = filterAs(results, asOptionalStorageMeta)
|
|
137
|
+
.map(p => p as WithStorageMeta<Payload>)
|
|
138
|
+
.sort(PayloadBuilder.compareStorageMeta)
|
|
139
|
+
.at(-1)
|
|
140
|
+
expect(last).toBeDefined()
|
|
141
|
+
expect(state?.state.cursor).toBe(last?._sequence)
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
describe('with previous state', () => {
|
|
145
|
+
it.each([1, 2, 3])('returns next state and batch results', async (batch) => {
|
|
146
|
+
const all = (await archivist.all()).sort(PayloadBuilder.compareStorageMeta)
|
|
147
|
+
const batchOffset = all.at((3 * batch) - 1)
|
|
148
|
+
expect(batchOffset).toBeDefined()
|
|
149
|
+
// Test across all offsets
|
|
150
|
+
const cursor = assertEx(batchOffset)._sequence
|
|
151
|
+
const lastState: ModuleState<ImageThumbnailDivinerState> = { schema: ModuleStateSchema, state: { cursor } }
|
|
152
|
+
const results = await sut.divine([lastState])
|
|
153
|
+
|
|
154
|
+
// Validate expected results length
|
|
155
|
+
// [BW, ImageThumbnail, TimeStamp] + 1 [ModuleState]
|
|
156
|
+
const expectedResults = testCases.slice(batch).flat().length + 1
|
|
157
|
+
expect(results.length).toBe(expectedResults)
|
|
158
|
+
|
|
159
|
+
// Validate expected state
|
|
160
|
+
const nextState = results.find(isModuleState<ImageThumbnailDivinerState>)
|
|
161
|
+
expect(nextState).toBeDefined()
|
|
162
|
+
expect(nextState?.state?.cursor).toBeDefined()
|
|
163
|
+
expect(nextState?.state.cursor).toBe(testCases?.at(-1)?.at(-1)?._sequence)
|
|
164
|
+
|
|
165
|
+
// Validate expected individual results
|
|
166
|
+
const bws = results.filter(isBoundWitness)
|
|
167
|
+
expect(bws.length).toBeGreaterThan(0)
|
|
168
|
+
const images = results.filter(isImageThumbnail)
|
|
169
|
+
expect(images.length).toBeGreaterThan(0)
|
|
170
|
+
const timestamps = results.filter(isTimestamp)
|
|
171
|
+
expect(timestamps.length).toBeGreaterThan(0)
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
})
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/* eslint-disable max-statements */
|
|
2
|
+
import '@xylabs/vitest-extended'
|
|
3
|
+
|
|
4
|
+
import { assertEx } from '@xylabs/assert'
|
|
5
|
+
import { delay } from '@xylabs/delay'
|
|
6
|
+
import { MemoryArchivist } from '@xyo-network/archivist-memory'
|
|
7
|
+
import { asArchivistInstance } from '@xyo-network/archivist-model'
|
|
8
|
+
import { BoundWitnessBuilder } from '@xyo-network/boundwitness-builder'
|
|
9
|
+
import { isBoundWitnessWithStorageMeta } from '@xyo-network/boundwitness-model'
|
|
10
|
+
import { MemoryBoundWitnessDiviner } from '@xyo-network/diviner-boundwitness-memory'
|
|
11
|
+
import { asDivinerInstance } from '@xyo-network/diviner-model'
|
|
12
|
+
import { GenericPayloadDiviner } from '@xyo-network/diviner-payload-generic'
|
|
13
|
+
import type {
|
|
14
|
+
ImageThumbnail,
|
|
15
|
+
ImageThumbnailDivinerQuery,
|
|
16
|
+
} from '@xyo-network/image-thumbnail-payload-plugin'
|
|
17
|
+
import {
|
|
18
|
+
ImageThumbnailDivinerQuerySchema,
|
|
19
|
+
isImageThumbnailResultIndex,
|
|
20
|
+
isImageThumbnailResultWithSources,
|
|
21
|
+
} from '@xyo-network/image-thumbnail-payload-plugin'
|
|
22
|
+
import type { PackageManifestPayload } from '@xyo-network/manifest'
|
|
23
|
+
import { ManifestWrapper } from '@xyo-network/manifest'
|
|
24
|
+
import { ModuleFactoryLocator } from '@xyo-network/module-factory-locator'
|
|
25
|
+
import type { ModuleState } from '@xyo-network/module-model'
|
|
26
|
+
import { isModuleState } from '@xyo-network/module-model'
|
|
27
|
+
import type { MemoryNode } from '@xyo-network/node-memory'
|
|
28
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
29
|
+
import type { Payload, WithStorageMeta } from '@xyo-network/payload-model'
|
|
30
|
+
import { HDWallet } from '@xyo-network/wallet'
|
|
31
|
+
import type { TimeStamp } from '@xyo-network/witness-timestamp'
|
|
32
|
+
import { TimestampSchema } from '@xyo-network/witness-timestamp'
|
|
33
|
+
import {
|
|
34
|
+
beforeAll,
|
|
35
|
+
describe, expect,
|
|
36
|
+
it,
|
|
37
|
+
} from 'vitest'
|
|
38
|
+
|
|
39
|
+
import { ImageThumbnailDiviner } from '../Diviner.ts'
|
|
40
|
+
import { ImageThumbnailIndexCandidateToImageThumbnailIndexDiviner } from '../ImageThumbnailIndexCandidateToImageThumbnailIndexDiviner.ts'
|
|
41
|
+
import { ImageThumbnailIndexQueryResponseToImageThumbnailQueryResponseDiviner }
|
|
42
|
+
from '../ImageThumbnailIndexQueryResponseToImageThumbnailQueryResponseDiviner.ts'
|
|
43
|
+
import { ImageThumbnailQueryToImageThumbnailIndexQueryDiviner } from '../ImageThumbnailQueryToImageThumbnailIndexQueryDiviner.ts'
|
|
44
|
+
import { ImageThumbnailStateToIndexCandidateDiviner } from '../ImageThumbnailStateToIndexCandidateDiviner/index.ts'
|
|
45
|
+
import imageThumbnailDivinerManifest from './ImageThumbnailDivinerManifest.json' with { type: 'json' }
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @group slow
|
|
49
|
+
*/
|
|
50
|
+
describe('ImageThumbnailDiviner', () => {
|
|
51
|
+
const sourceUrl = 'https://placekitten.com/200/300'
|
|
52
|
+
const thumbnailHttpSuccess: ImageThumbnail = {
|
|
53
|
+
http: { status: 200 },
|
|
54
|
+
schema: 'network.xyo.image.thumbnail',
|
|
55
|
+
sourceHash: '7f39363514d9d9b958a5a993edeba35cb44f912c7072ed9ddd628728ac0fd681',
|
|
56
|
+
sourceUrl,
|
|
57
|
+
url: 'data:image/png;base64,===',
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const thumbnailHttpFail: ImageThumbnail = {
|
|
61
|
+
http: {
|
|
62
|
+
ipAddress: '104.17.96.13',
|
|
63
|
+
status: 429,
|
|
64
|
+
},
|
|
65
|
+
schema: 'network.xyo.image.thumbnail',
|
|
66
|
+
sourceUrl,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const thumbnailCodeFail: ImageThumbnail = {
|
|
70
|
+
http: { code: 'FAILED' },
|
|
71
|
+
schema: 'network.xyo.image.thumbnail',
|
|
72
|
+
sourceUrl,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const thumbnailWitnessFail: ImageThumbnail = {
|
|
76
|
+
http: { ipAddress: '104.17.96.13' },
|
|
77
|
+
schema: 'network.xyo.image.thumbnail',
|
|
78
|
+
sourceUrl,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let testCases: WithStorageMeta<Payload>[][] = []
|
|
82
|
+
let archivist: MemoryArchivist
|
|
83
|
+
let node: MemoryNode
|
|
84
|
+
let sut: ImageThumbnailDiviner
|
|
85
|
+
|
|
86
|
+
beforeAll(async () => {
|
|
87
|
+
const wallet = await HDWallet.random()
|
|
88
|
+
const locator = new ModuleFactoryLocator()
|
|
89
|
+
locator.register(MemoryArchivist.factory())
|
|
90
|
+
locator.register(MemoryBoundWitnessDiviner.factory())
|
|
91
|
+
locator.register(GenericPayloadDiviner.factory())
|
|
92
|
+
locator.register(ImageThumbnailIndexCandidateToImageThumbnailIndexDiviner.factory())
|
|
93
|
+
locator.register(ImageThumbnailIndexQueryResponseToImageThumbnailQueryResponseDiviner.factory())
|
|
94
|
+
locator.register(ImageThumbnailQueryToImageThumbnailIndexQueryDiviner.factory())
|
|
95
|
+
locator.register(ImageThumbnailStateToIndexCandidateDiviner.factory())
|
|
96
|
+
locator.register(ImageThumbnailDiviner.factory())
|
|
97
|
+
const manifest = imageThumbnailDivinerManifest as PackageManifestPayload
|
|
98
|
+
const manifestWrapper = new ManifestWrapper(manifest, wallet, locator)
|
|
99
|
+
node = await manifestWrapper.loadNodeFromIndex(0)
|
|
100
|
+
await node.start()
|
|
101
|
+
|
|
102
|
+
const privateModules = manifest.nodes[0].modules?.private ?? []
|
|
103
|
+
const publicModules = manifest.nodes[0].modules?.public ?? []
|
|
104
|
+
const mods = await node.resolve('*')
|
|
105
|
+
expect(mods.length).toBe(privateModules.length + publicModules.length + 1)
|
|
106
|
+
|
|
107
|
+
// Insert previously witnessed payloads into thumbnail archivist
|
|
108
|
+
const httpSuccessTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: 1 }
|
|
109
|
+
const [httpSuccessBoundWitness, httpSuccessPayloads] = await (new BoundWitnessBuilder().payloads([thumbnailHttpSuccess, httpSuccessTimestamp])).build()
|
|
110
|
+
|
|
111
|
+
const httpFailTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: 2 }
|
|
112
|
+
const [httpFailBoundWitness, httpFailPayloads] = await (new BoundWitnessBuilder().payloads([thumbnailHttpFail, httpFailTimestamp])).build()
|
|
113
|
+
|
|
114
|
+
const witnessFailTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: 3 }
|
|
115
|
+
const [witnessFailBoundWitness, witnessFailPayloads] = await (new BoundWitnessBuilder().payloads([thumbnailWitnessFail, witnessFailTimestamp])).build()
|
|
116
|
+
|
|
117
|
+
const codeFailTimestamp: TimeStamp = { schema: TimestampSchema, timestamp: 4 }
|
|
118
|
+
const [codeFailBoundWitness, codeFailPayloads] = await (new BoundWitnessBuilder().payloads([thumbnailCodeFail, codeFailTimestamp])).build()
|
|
119
|
+
|
|
120
|
+
archivist = assertEx(asArchivistInstance<MemoryArchivist>(await node.resolve('ImageThumbnailArchivist')))
|
|
121
|
+
const testCasesToCreate = [
|
|
122
|
+
[httpSuccessBoundWitness, ...httpSuccessPayloads],
|
|
123
|
+
[httpFailBoundWitness, ...httpFailPayloads],
|
|
124
|
+
[witnessFailBoundWitness, ...witnessFailPayloads],
|
|
125
|
+
[codeFailBoundWitness, ...codeFailPayloads],
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
for (const [bw, ...payloads] of testCasesToCreate) {
|
|
129
|
+
const createdTestCase = []
|
|
130
|
+
for (const payload of [bw, ...payloads]) {
|
|
131
|
+
await delay(2)
|
|
132
|
+
const [signedPayload] = await archivist.insert([payload])
|
|
133
|
+
createdTestCase.push(signedPayload)
|
|
134
|
+
}
|
|
135
|
+
testCases.push(createdTestCase)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
sut = assertEx(asDivinerInstance<ImageThumbnailDiviner>(await node.resolve('ImageThumbnailDiviner')))
|
|
139
|
+
|
|
140
|
+
// Allow enough time for diviner to divine
|
|
141
|
+
await delay(5000)
|
|
142
|
+
}, 40_000)
|
|
143
|
+
describe('diviner state', () => {
|
|
144
|
+
let stateArchivist: MemoryArchivist
|
|
145
|
+
beforeAll(async () => {
|
|
146
|
+
const mod = await node.resolve('AddressStateArchivist')
|
|
147
|
+
stateArchivist = assertEx(asArchivistInstance<MemoryArchivist>(mod))
|
|
148
|
+
})
|
|
149
|
+
it('has expected bound witnesses', async () => {
|
|
150
|
+
const payloads = await stateArchivist.all()
|
|
151
|
+
const stateBoundWitnesses = payloads.filter(isBoundWitnessWithStorageMeta)
|
|
152
|
+
expect(stateBoundWitnesses).toBeArrayOfSize(1)
|
|
153
|
+
for (const stateBoundWitness of stateBoundWitnesses) {
|
|
154
|
+
expect(stateBoundWitness).toBeObject()
|
|
155
|
+
expect(stateBoundWitness.addresses).toBeArrayOfSize(1)
|
|
156
|
+
expect(stateBoundWitness.addresses).toContain(sut.address)
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
it('has expected state', async () => {
|
|
160
|
+
const payloads = await stateArchivist.all()
|
|
161
|
+
const statePayloads = payloads.filter(isModuleState) as WithStorageMeta<ModuleState>[]
|
|
162
|
+
expect(statePayloads).toBeArrayOfSize(1)
|
|
163
|
+
expect(statePayloads.at(-1)).toBeObject()
|
|
164
|
+
const statePayload = assertEx(statePayloads.at(-1))
|
|
165
|
+
expect(statePayload.state).toBeObject()
|
|
166
|
+
const lastCursor = PayloadBuilder.sortByStorageMeta(testCases.flat()).at(-1)?._sequence
|
|
167
|
+
expect(statePayload.state?.cursor).toBe(lastCursor)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
describe('diviner index', () => {
|
|
171
|
+
let indexArchivist: MemoryArchivist
|
|
172
|
+
beforeAll(async () => {
|
|
173
|
+
const mod = await node.resolve('ImageThumbnailDivinerIndexArchivist')
|
|
174
|
+
indexArchivist = assertEx(asArchivistInstance<MemoryArchivist>(mod))
|
|
175
|
+
})
|
|
176
|
+
// NOTE: We're not signing indexes for performance reasons
|
|
177
|
+
it.skip('has expected bound witnesses', async () => {
|
|
178
|
+
const payloads = await indexArchivist.all()
|
|
179
|
+
const indexBoundWitnesses = payloads.filter(isBoundWitnessWithStorageMeta)
|
|
180
|
+
expect(indexBoundWitnesses).toBeArrayOfSize(1)
|
|
181
|
+
const indexBoundWitness = indexBoundWitnesses[0]
|
|
182
|
+
expect(indexBoundWitness).toBeObject()
|
|
183
|
+
expect(indexBoundWitness.addresses).toBeArrayOfSize(1)
|
|
184
|
+
expect(indexBoundWitness.addresses).toContain(sut.address)
|
|
185
|
+
})
|
|
186
|
+
it('has expected index', async () => {
|
|
187
|
+
const payloads = await indexArchivist.all()
|
|
188
|
+
const indexPayloads = payloads.filter(isImageThumbnailResultIndex)
|
|
189
|
+
expect(indexPayloads).toBeArrayOfSize(testCases.length)
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
describe('with no thumbnail for the provided URL', () => {
|
|
193
|
+
const url = 'https://does.not.exist.io'
|
|
194
|
+
const schema = ImageThumbnailDivinerQuerySchema
|
|
195
|
+
it('returns nothing', async () => {
|
|
196
|
+
const query: ImageThumbnailDivinerQuery = { schema, url }
|
|
197
|
+
const result = await sut.divine([query])
|
|
198
|
+
expect(result).toBeArrayOfSize(0)
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
describe('with thumbnails for the provided URL', () => {
|
|
202
|
+
const url = sourceUrl
|
|
203
|
+
const schema = ImageThumbnailDivinerQuerySchema
|
|
204
|
+
describe('with no filter criteria', () => {
|
|
205
|
+
it('returns the most recent success', async () => {
|
|
206
|
+
const query: ImageThumbnailDivinerQuery = {
|
|
207
|
+
schema, success: true, url,
|
|
208
|
+
}
|
|
209
|
+
const results = await sut.divine([query])
|
|
210
|
+
const result = results.find(isImageThumbnailResultWithSources)
|
|
211
|
+
expect(result).toBeDefined()
|
|
212
|
+
const expected = await PayloadBuilder.dataHash(thumbnailHttpSuccess)
|
|
213
|
+
expect(result?.$sources).toContain(expected)
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
describe('with filter criteria', () => {
|
|
217
|
+
describe('for status code', () => {
|
|
218
|
+
const cases: ImageThumbnail[] = [thumbnailHttpSuccess, thumbnailHttpFail]
|
|
219
|
+
it.each(cases)('returns the most recent instance of that status code', async (payload) => {
|
|
220
|
+
const { status } = payload.http ?? {}
|
|
221
|
+
const query: ImageThumbnailDivinerQuery = {
|
|
222
|
+
schema, status, url,
|
|
223
|
+
}
|
|
224
|
+
const results = await sut.divine([query])
|
|
225
|
+
const result = results.find(isImageThumbnailResultWithSources)
|
|
226
|
+
expect(result).toBeDefined()
|
|
227
|
+
const expected = await PayloadBuilder.dataHash(payload)
|
|
228
|
+
expect(result?.$sources).toContain(expected)
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
describe('for success (most recent)', () => {
|
|
232
|
+
const cases: ImageThumbnail[] = [thumbnailHttpSuccess]
|
|
233
|
+
it.each(cases)('returns the most recent instance of that success state', async (payload) => {
|
|
234
|
+
const success = !!(payload.url ?? false)
|
|
235
|
+
const query: ImageThumbnailDivinerQuery = {
|
|
236
|
+
schema, success, url,
|
|
237
|
+
}
|
|
238
|
+
const results = await sut.divine([query])
|
|
239
|
+
const result = results.find(isImageThumbnailResultWithSources)
|
|
240
|
+
expect(result).toBeDefined()
|
|
241
|
+
const expected = await PayloadBuilder.dataHash(payload)
|
|
242
|
+
expect(result?.$sources).toContain(expected)
|
|
243
|
+
})
|
|
244
|
+
})
|
|
245
|
+
describe('for failure (most recent)', () => {
|
|
246
|
+
const cases: ImageThumbnail[] = [thumbnailCodeFail]
|
|
247
|
+
it.each(cases)('returns the most recent instance of that success state', async (payload) => {
|
|
248
|
+
const success = !!(payload.url ?? false)
|
|
249
|
+
const query: ImageThumbnailDivinerQuery = {
|
|
250
|
+
schema, success, url,
|
|
251
|
+
}
|
|
252
|
+
const results = await sut.divine([query])
|
|
253
|
+
const result = results.find(isImageThumbnailResultWithSources)
|
|
254
|
+
expect(result).toBeDefined()
|
|
255
|
+
const expected = await PayloadBuilder.dataHash(payload)
|
|
256
|
+
expect(result?.$sources).toContain(expected)
|
|
257
|
+
})
|
|
258
|
+
})
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
})
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import { BoundWitnessBuilder } from '@xyo-network/boundwitness-builder'
|
|
4
|
+
import type { BoundWitness } from '@xyo-network/boundwitness-model'
|
|
5
|
+
import type { ImageThumbnail } from '@xyo-network/image-thumbnail-payload-plugin'
|
|
6
|
+
import { ImageThumbnailSchema, isImageThumbnailResultIndexWithSources } from '@xyo-network/image-thumbnail-payload-plugin'
|
|
7
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
8
|
+
import type { Payload } from '@xyo-network/payload-model'
|
|
9
|
+
import { UrlSchema } from '@xyo-network/url-payload-plugin'
|
|
10
|
+
import type { TimeStamp } from '@xyo-network/witness-timestamp'
|
|
11
|
+
import { TimestampSchema } from '@xyo-network/witness-timestamp'
|
|
12
|
+
import {
|
|
13
|
+
beforeAll,
|
|
14
|
+
describe, expect, it,
|
|
15
|
+
} from 'vitest'
|
|
16
|
+
|
|
17
|
+
import { ImageThumbnailIndexCandidateToImageThumbnailIndexDiviner } from '../ImageThumbnailIndexCandidateToImageThumbnailIndexDiviner.ts'
|
|
18
|
+
|
|
19
|
+
describe('ImageThumbnailIndexCandidateToImageThumbnailIndexDiviner', () => {
|
|
20
|
+
let diviner: ImageThumbnailIndexCandidateToImageThumbnailIndexDiviner
|
|
21
|
+
const timestampA = 1_234_567_890
|
|
22
|
+
const timestampPayloadA: TimeStamp = { schema: TimestampSchema, timestamp: timestampA }
|
|
23
|
+
const imageThumbnailPayloadA: ImageThumbnail = {
|
|
24
|
+
http: { status: 200 },
|
|
25
|
+
schema: ImageThumbnailSchema,
|
|
26
|
+
sourceUrl: 'https://xyo.network',
|
|
27
|
+
url: 'data',
|
|
28
|
+
}
|
|
29
|
+
const timestampB = 1_234_567_891
|
|
30
|
+
const timestampPayloadB: TimeStamp = { schema: TimestampSchema, timestamp: timestampB }
|
|
31
|
+
const imageThumbnailPayloadB: ImageThumbnail = {
|
|
32
|
+
http: { status: 500 },
|
|
33
|
+
schema: ImageThumbnailSchema,
|
|
34
|
+
sourceUrl: 'https://xyo.network',
|
|
35
|
+
}
|
|
36
|
+
const validateResult = async (input: [boundWitness: BoundWitness, thumbnail: ImageThumbnail, timestamp: TimeStamp], result: Payload[]) => {
|
|
37
|
+
const [boundWitness, thumbnail, timestamp] = input
|
|
38
|
+
const payloadDictionary = await PayloadBuilder.toDataHashMap([boundWitness, thumbnail, timestamp])
|
|
39
|
+
expect(result).toBeArrayOfSize(1)
|
|
40
|
+
expect(result.filter(isImageThumbnailResultIndexWithSources)).toBeArrayOfSize(1)
|
|
41
|
+
const index = result.find(isImageThumbnailResultIndexWithSources)
|
|
42
|
+
const key = await PayloadBuilder.dataHash({ schema: UrlSchema, url: thumbnail.sourceUrl })
|
|
43
|
+
expect(index).toBeDefined()
|
|
44
|
+
if (index !== undefined) {
|
|
45
|
+
expect(index.key).toBe(key)
|
|
46
|
+
expect(index.$sources.toSorted()).toEqual(Object.keys(payloadDictionary).toSorted())
|
|
47
|
+
expect(index.success).toBe(thumbnail.http?.status === 200)
|
|
48
|
+
expect(index.timestamp).toBe(timestamp.timestamp)
|
|
49
|
+
expect(index.status).toBe(thumbnail.http?.status)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
beforeAll(async () => {
|
|
53
|
+
diviner = await ImageThumbnailIndexCandidateToImageThumbnailIndexDiviner.create({ account: 'random' })
|
|
54
|
+
})
|
|
55
|
+
describe('divine', () => {
|
|
56
|
+
const cases: [ImageThumbnail, TimeStamp][] = [
|
|
57
|
+
[imageThumbnailPayloadA, timestampPayloadA],
|
|
58
|
+
[imageThumbnailPayloadB, timestampPayloadB],
|
|
59
|
+
]
|
|
60
|
+
describe('with single result', () => {
|
|
61
|
+
it.each(cases)('transforms single result', async (thumbnail, timestamp) => {
|
|
62
|
+
const [boundWitness] = await (new BoundWitnessBuilder().payloads([thumbnail, timestamp])).build()
|
|
63
|
+
const result = await diviner.divine([boundWitness, thumbnail, timestamp])
|
|
64
|
+
await validateResult([boundWitness, thumbnail, timestamp], result)
|
|
65
|
+
})
|
|
66
|
+
it.each(cases)('handles sparse inputs', async (thumbnail, timestamp) => {
|
|
67
|
+
const [boundWitness] = await (new BoundWitnessBuilder().payloads([thumbnail, timestamp])).build()
|
|
68
|
+
expect(await diviner.divine([thumbnail, timestamp])).toBeArrayOfSize(0)
|
|
69
|
+
expect(await diviner.divine([boundWitness, timestamp])).toBeArrayOfSize(0)
|
|
70
|
+
expect(await diviner.divine([boundWitness, thumbnail])).toBeArrayOfSize(0)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
describe('with multiple results', () => {
|
|
74
|
+
it('transforms multiple results', async () => {
|
|
75
|
+
const data: [BoundWitness, ImageThumbnail, TimeStamp][] = await Promise.all(
|
|
76
|
+
cases.map(async (payloads) => {
|
|
77
|
+
const [bw] = await (new BoundWitnessBuilder().payloads(payloads)).build()
|
|
78
|
+
return [bw, ...payloads]
|
|
79
|
+
}),
|
|
80
|
+
)
|
|
81
|
+
const results = await diviner.divine(data.flat())
|
|
82
|
+
expect(results).toBeArrayOfSize(2)
|
|
83
|
+
await Promise.all(
|
|
84
|
+
data.map(async (input, i) => {
|
|
85
|
+
const result = results[i]
|
|
86
|
+
await validateResult(input, [result])
|
|
87
|
+
}),
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
it('handles sparse inputs', async () => {
|
|
91
|
+
const [bw] = await (new BoundWitnessBuilder().payloads(cases[0])).build()
|
|
92
|
+
const results = await diviner.divine([bw, ...cases.flat()])
|
|
93
|
+
expect(results).toBeArrayOfSize(1)
|
|
94
|
+
await validateResult([bw, ...cases[0]], results)
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
})
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import type { Hash } from '@xylabs/hex'
|
|
4
|
+
import type {
|
|
5
|
+
ImageThumbnailDivinerQuery,
|
|
6
|
+
ImageThumbnailResult,
|
|
7
|
+
ImageThumbnailResultIndex,
|
|
8
|
+
} from '@xyo-network/image-thumbnail-payload-plugin'
|
|
9
|
+
import {
|
|
10
|
+
ImageThumbnailDivinerQuerySchema,
|
|
11
|
+
ImageThumbnailResultIndexSchema,
|
|
12
|
+
ImageThumbnailResultSchema,
|
|
13
|
+
isImageThumbnailResult,
|
|
14
|
+
} from '@xyo-network/image-thumbnail-payload-plugin'
|
|
15
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
16
|
+
import type { WithSources } from '@xyo-network/payload-model'
|
|
17
|
+
import { UrlSchema } from '@xyo-network/url-payload-plugin'
|
|
18
|
+
import {
|
|
19
|
+
beforeAll,
|
|
20
|
+
describe, expect, it,
|
|
21
|
+
} from 'vitest'
|
|
22
|
+
|
|
23
|
+
import { ImageThumbnailIndexQueryResponseToImageThumbnailQueryResponseDiviner }
|
|
24
|
+
from '../ImageThumbnailIndexQueryResponseToImageThumbnailQueryResponseDiviner.ts'
|
|
25
|
+
|
|
26
|
+
describe('ImageThumbnailIndexQueryResponseToImageThumbnailQueryResponseDiviner', () => {
|
|
27
|
+
const queries: ImageThumbnailDivinerQuery[] = [
|
|
28
|
+
{
|
|
29
|
+
schema: ImageThumbnailDivinerQuerySchema,
|
|
30
|
+
url: 'https://xyo.network',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
schema: ImageThumbnailDivinerQuerySchema,
|
|
34
|
+
url: 'https://explore.xyo.network',
|
|
35
|
+
},
|
|
36
|
+
]
|
|
37
|
+
const indexes: WithSources<ImageThumbnailResultIndex>[][] = [
|
|
38
|
+
[
|
|
39
|
+
{
|
|
40
|
+
key: 'setInBeforeAll' as Hash,
|
|
41
|
+
schema: ImageThumbnailResultIndexSchema,
|
|
42
|
+
$sources: [],
|
|
43
|
+
status: 200,
|
|
44
|
+
success: true,
|
|
45
|
+
timestamp: 1_234_567_890,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
[
|
|
49
|
+
{
|
|
50
|
+
key: 'setInBeforeAll' as Hash,
|
|
51
|
+
schema: ImageThumbnailResultIndexSchema,
|
|
52
|
+
$sources: [],
|
|
53
|
+
status: 200,
|
|
54
|
+
success: true,
|
|
55
|
+
timestamp: 1_234_567_891,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
key: 'setInBeforeAll' as Hash,
|
|
59
|
+
schema: ImageThumbnailResultIndexSchema,
|
|
60
|
+
$sources: [],
|
|
61
|
+
status: 500,
|
|
62
|
+
success: false,
|
|
63
|
+
timestamp: 1_234_567_892,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
]
|
|
67
|
+
let diviner: ImageThumbnailIndexQueryResponseToImageThumbnailQueryResponseDiviner
|
|
68
|
+
beforeAll(async () => {
|
|
69
|
+
diviner = await ImageThumbnailIndexQueryResponseToImageThumbnailQueryResponseDiviner.create({ account: 'random' })
|
|
70
|
+
await Promise.all(
|
|
71
|
+
queries.map(async (query, i) => {
|
|
72
|
+
await Promise.all(
|
|
73
|
+
indexes[i].map(async (index) => {
|
|
74
|
+
index.key = await PayloadBuilder.dataHash({ schema: UrlSchema, url: query.url })
|
|
75
|
+
}),
|
|
76
|
+
)
|
|
77
|
+
}),
|
|
78
|
+
)
|
|
79
|
+
})
|
|
80
|
+
const cases: [ImageThumbnailDivinerQuery, ImageThumbnailResultIndex[]][] = queries.map((query, i) => [query, indexes[i]])
|
|
81
|
+
describe('divine', () => {
|
|
82
|
+
describe('with single url in index result', () => {
|
|
83
|
+
it.each(cases)('transforms single url index results', async (imageThumbnailDivinerQuery, imageThumbnailResultIndex) => {
|
|
84
|
+
const results = await diviner.divine([imageThumbnailDivinerQuery, ...imageThumbnailResultIndex])
|
|
85
|
+
expect(results).toBeArrayOfSize(imageThumbnailResultIndex.length)
|
|
86
|
+
expect(results.filter(isImageThumbnailResult)).toBeArrayOfSize(imageThumbnailResultIndex.length)
|
|
87
|
+
for (const [i, result] of (results.filter(isImageThumbnailResult) as ImageThumbnailResult[]).entries()) {
|
|
88
|
+
const index = imageThumbnailResultIndex[i]
|
|
89
|
+
expect(result.url).toBe(imageThumbnailDivinerQuery.url)
|
|
90
|
+
expect(result.success).toBe(index.success)
|
|
91
|
+
expect(result.timestamp).toBe(index.timestamp)
|
|
92
|
+
expect(result.status).toBe(index.status)
|
|
93
|
+
expect(result.schema).toBe(ImageThumbnailResultSchema)
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
describe('with multiple urls in index result', () => {
|
|
98
|
+
it('transforms multiple url index results', async () => {
|
|
99
|
+
const indexesLength = indexes.flat().length
|
|
100
|
+
const results = await diviner.divine([...queries, ...indexes.flat()])
|
|
101
|
+
expect(results).toBeArrayOfSize(indexesLength)
|
|
102
|
+
const resultsIndexes = results.filter(isImageThumbnailResult) as ImageThumbnailResult[]
|
|
103
|
+
expect(resultsIndexes).toBeArrayOfSize(indexesLength)
|
|
104
|
+
let resultsIterator = 0
|
|
105
|
+
for (const [i, { url }] of queries.entries()) {
|
|
106
|
+
const indexSet = indexes[i]
|
|
107
|
+
for (const index of indexSet) {
|
|
108
|
+
const result = resultsIndexes[resultsIterator]
|
|
109
|
+
expect(result.url).toBe(url)
|
|
110
|
+
expect(result.success).toBe(index.success)
|
|
111
|
+
expect(result.timestamp).toBe(index.timestamp)
|
|
112
|
+
expect(result.status).toBe(index.status)
|
|
113
|
+
expect(result.schema).toBe(ImageThumbnailResultSchema)
|
|
114
|
+
resultsIterator = ++resultsIterator
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
})
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import type { ImageThumbnailDivinerQuery } from '@xyo-network/image-thumbnail-payload-plugin'
|
|
4
|
+
import { ImageThumbnailDivinerQuerySchema } from '@xyo-network/image-thumbnail-payload-plugin'
|
|
5
|
+
import { PayloadBuilder } from '@xyo-network/payload-builder'
|
|
6
|
+
import { UrlSchema } from '@xyo-network/url-payload-plugin'
|
|
7
|
+
import {
|
|
8
|
+
beforeAll,
|
|
9
|
+
describe, expect, it,
|
|
10
|
+
} from 'vitest'
|
|
11
|
+
|
|
12
|
+
import { ImageThumbnailQueryToImageThumbnailIndexQueryDiviner } from '../ImageThumbnailQueryToImageThumbnailIndexQueryDiviner.ts'
|
|
13
|
+
import type { ImageThumbnailResultQuery } from '../ImageThumbnailResultQuery.ts'
|
|
14
|
+
import { isImageThumbnailResultQuery } from '../ImageThumbnailResultQuery.ts'
|
|
15
|
+
|
|
16
|
+
describe('ImageThumbnailQueryToImageThumbnailIndexQueryDiviner', () => {
|
|
17
|
+
let diviner: ImageThumbnailQueryToImageThumbnailIndexQueryDiviner
|
|
18
|
+
const queries: ImageThumbnailDivinerQuery[] = [
|
|
19
|
+
{
|
|
20
|
+
schema: ImageThumbnailDivinerQuerySchema,
|
|
21
|
+
url: 'https://xyo.network',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
limit: 10,
|
|
25
|
+
order: 'asc',
|
|
26
|
+
schema: ImageThumbnailDivinerQuerySchema,
|
|
27
|
+
status: 200,
|
|
28
|
+
success: true,
|
|
29
|
+
url: 'https://xyo.network',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
limit: 10,
|
|
33
|
+
schema: ImageThumbnailDivinerQuerySchema,
|
|
34
|
+
url: 'https://explore.xyo.network',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
schema: ImageThumbnailDivinerQuerySchema,
|
|
38
|
+
url: 'https://explore.xyo.network',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
order: 'asc',
|
|
42
|
+
schema: ImageThumbnailDivinerQuerySchema,
|
|
43
|
+
url: 'https://explore.xyo.network',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
schema: ImageThumbnailDivinerQuerySchema,
|
|
47
|
+
status: 200,
|
|
48
|
+
url: 'https://explore.xyo.network',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
schema: ImageThumbnailDivinerQuerySchema,
|
|
52
|
+
success: true,
|
|
53
|
+
url: 'https://explore.xyo.network',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
schema: ImageThumbnailDivinerQuerySchema,
|
|
57
|
+
success: false,
|
|
58
|
+
url: 'https://explore.xyo.network',
|
|
59
|
+
},
|
|
60
|
+
]
|
|
61
|
+
const expected: ImageThumbnailResultQuery[] = [
|
|
62
|
+
{
|
|
63
|
+
key: 'setInBeforeAll',
|
|
64
|
+
limit: 1,
|
|
65
|
+
order: 'desc',
|
|
66
|
+
schema: 'network.xyo.diviner.payload.query',
|
|
67
|
+
} as unknown as ImageThumbnailResultQuery,
|
|
68
|
+
{
|
|
69
|
+
key: 'setInBeforeAll',
|
|
70
|
+
limit: 10,
|
|
71
|
+
order: 'asc',
|
|
72
|
+
schema: 'network.xyo.diviner.payload.query',
|
|
73
|
+
status: 200,
|
|
74
|
+
success: true,
|
|
75
|
+
} as unknown as ImageThumbnailResultQuery,
|
|
76
|
+
{
|
|
77
|
+
key: 'setInBeforeAll',
|
|
78
|
+
limit: 10,
|
|
79
|
+
order: 'desc',
|
|
80
|
+
schema: 'network.xyo.diviner.payload.query',
|
|
81
|
+
} as unknown as ImageThumbnailResultQuery,
|
|
82
|
+
{
|
|
83
|
+
key: 'setInBeforeAll',
|
|
84
|
+
limit: 1,
|
|
85
|
+
order: 'desc',
|
|
86
|
+
schema: 'network.xyo.diviner.payload.query',
|
|
87
|
+
} as unknown as ImageThumbnailResultQuery,
|
|
88
|
+
{
|
|
89
|
+
key: 'setInBeforeAll',
|
|
90
|
+
limit: 1,
|
|
91
|
+
order: 'asc',
|
|
92
|
+
schema: 'network.xyo.diviner.payload.query',
|
|
93
|
+
} as unknown as ImageThumbnailResultQuery,
|
|
94
|
+
{
|
|
95
|
+
key: 'setInBeforeAll',
|
|
96
|
+
limit: 1,
|
|
97
|
+
order: 'desc',
|
|
98
|
+
schema: 'network.xyo.diviner.payload.query',
|
|
99
|
+
status: 200,
|
|
100
|
+
} as unknown as ImageThumbnailResultQuery,
|
|
101
|
+
{
|
|
102
|
+
key: 'setInBeforeAll',
|
|
103
|
+
limit: 1,
|
|
104
|
+
order: 'desc',
|
|
105
|
+
schema: 'network.xyo.diviner.payload.query',
|
|
106
|
+
success: true,
|
|
107
|
+
} as unknown as ImageThumbnailResultQuery,
|
|
108
|
+
{
|
|
109
|
+
key: 'setInBeforeAll',
|
|
110
|
+
limit: 1,
|
|
111
|
+
order: 'desc',
|
|
112
|
+
schema: 'network.xyo.diviner.payload.query',
|
|
113
|
+
success: false,
|
|
114
|
+
} as unknown as ImageThumbnailResultQuery,
|
|
115
|
+
]
|
|
116
|
+
const cases: [ImageThumbnailDivinerQuery, ImageThumbnailResultQuery][] = queries.map((query, i) => [query, expected[i]])
|
|
117
|
+
beforeAll(async () => {
|
|
118
|
+
diviner = await ImageThumbnailQueryToImageThumbnailIndexQueryDiviner.create({ account: 'random' })
|
|
119
|
+
|
|
120
|
+
await Promise.all(
|
|
121
|
+
queries.map(async (query, i) => {
|
|
122
|
+
const key = await PayloadBuilder.dataHash({ schema: UrlSchema, url: query.url })
|
|
123
|
+
expected[i].key = key
|
|
124
|
+
}),
|
|
125
|
+
)
|
|
126
|
+
})
|
|
127
|
+
describe('divine', () => {
|
|
128
|
+
describe('with single query', () => {
|
|
129
|
+
it.each(cases)('transforms query', async (query, expected) => {
|
|
130
|
+
const results = await diviner.divine([query])
|
|
131
|
+
const actual = results.filter(isImageThumbnailResultQuery)
|
|
132
|
+
expect(actual).toBeArrayOfSize(1)
|
|
133
|
+
expect(await PayloadBuilder.dataHash(actual?.[0])).toEqual(await PayloadBuilder.dataHash(expected))
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
describe('with multiple queries', () => {
|
|
137
|
+
it('transforms queries', async () => {
|
|
138
|
+
const results = await diviner.divine(queries)
|
|
139
|
+
const actual = results.filter(isImageThumbnailResultQuery)
|
|
140
|
+
expect(actual).toBeArrayOfSize(expected.length)
|
|
141
|
+
expect(await PayloadBuilder.dataHashes(actual)).toEqual(await PayloadBuilder.dataHashes(expected))
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
})
|
package/typedoc.json
DELETED