@avelonjs/conformance 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.
@@ -0,0 +1,167 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import {
3
+ Invalid,
4
+ type AiCapabilities,
5
+ type AiDriver,
6
+ type CompletionAiSurface,
7
+ type CompletionChunk,
8
+ type CompletionRequest,
9
+ type EmbeddingAiSurface,
10
+ type StreamingAiSurface,
11
+ } from '@avelonjs/core'
12
+ import { assertCapabilitySurface, captureFailure, type SuiteContext } from '../harness'
13
+
14
+ type AssayDriver = AiDriver<AiCapabilities> &
15
+ Partial<CompletionAiSurface & EmbeddingAiSurface & StreamingAiSurface>
16
+
17
+ function assay<TDriver extends AssayDriver>(
18
+ context: SuiteContext<TDriver>,
19
+ name: string,
20
+ assertion: (driver: TDriver) => Promise<void> | void,
21
+ ): void {
22
+ test(name, async () => {
23
+ const driver = await context.create()
24
+ try {
25
+ await assertion(driver)
26
+ } finally {
27
+ await context.cleanup?.(driver)
28
+ }
29
+ })
30
+ }
31
+
32
+ function hasCompletion(driver: AssayDriver): driver is AssayDriver & CompletionAiSurface {
33
+ return driver.capabilities.modes.includes('completion') && typeof driver.complete === 'function'
34
+ }
35
+
36
+ function hasEmbedding(driver: AssayDriver): driver is AssayDriver & EmbeddingAiSurface {
37
+ return driver.capabilities.modes.includes('embedding') && typeof driver.embed === 'function'
38
+ }
39
+
40
+ function hasStreaming(driver: AssayDriver): driver is AssayDriver & StreamingAiSurface {
41
+ return (
42
+ driver.capabilities.modes.includes('completion') &&
43
+ driver.capabilities.streaming &&
44
+ typeof driver.stream === 'function'
45
+ )
46
+ }
47
+
48
+ async function expectInvalid(operation: () => Promise<unknown>): Promise<Invalid> {
49
+ const error = await captureFailure(operation)
50
+ expect(error).toBeInstanceOf(Invalid)
51
+ if (!(error instanceof Invalid)) throw new Error('Expected Invalid after taxonomy assertion.')
52
+ expect(error.code).toBe('INVALID')
53
+ return error
54
+ }
55
+
56
+ function request(): CompletionRequest {
57
+ return {
58
+ model: 'assay-model',
59
+ temperature: 0,
60
+ messages: [
61
+ { role: 'system', content: 'Answer directly.' },
62
+ { role: 'user', content: 'Explain ordered streaming.' },
63
+ ],
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Registers the portable AI contract conformance suite.
69
+ *
70
+ * The assay model is deterministic at temperature zero, allowing streamed increments to be joined
71
+ * and compared with the complete result. An embedding batch is "one or more" inputs in the frozen
72
+ * contract, so an empty batch is pinned to `Invalid`.
73
+ *
74
+ * @param context Fresh isolated AI drivers configured with the deterministic assay model.
75
+ */
76
+ export function aiSuite<TDriver extends AssayDriver>(context: SuiteContext<TDriver>): void {
77
+ describe(`ai conformance: ${context.name}`, () => {
78
+ assay(context, 'reports a present and stable resolved ai instance', (driver) => {
79
+ const instance = driver.instance
80
+ expect(instance.length).toBeGreaterThan(0)
81
+ expect(driver.instance).toBe(instance)
82
+ })
83
+
84
+ assay(context, 'enforces every ai capability surface in both directions', (driver) => {
85
+ const hasCompletionMode = driver.capabilities.modes.includes('completion')
86
+ const hasEmbeddingMode = driver.capabilities.modes.includes('embedding')
87
+ const surfaces: readonly [string, boolean, readonly string[]][] = [
88
+ ['completion', hasCompletionMode, ['complete']],
89
+ ['embedding', hasEmbeddingMode, ['embed']],
90
+ ['streaming', hasCompletionMode && driver.capabilities.streaming, ['stream']],
91
+ ]
92
+ for (const [capability, declared, methods] of surfaces) {
93
+ assertCapabilitySurface(driver, capability, declared, methods)
94
+ assertCapabilitySurface(
95
+ { capabilities: { [capability]: false } },
96
+ capability,
97
+ false,
98
+ methods,
99
+ )
100
+ expect(() =>
101
+ assertCapabilitySurface(
102
+ { capabilities: { [capability]: false }, [methods[0] ?? 'missing']: () => undefined },
103
+ capability,
104
+ false,
105
+ methods,
106
+ ),
107
+ ).toThrow()
108
+ expect(() =>
109
+ assertCapabilitySurface(
110
+ { capabilities: { [capability]: true } },
111
+ capability,
112
+ true,
113
+ methods,
114
+ ),
115
+ ).toThrow()
116
+ }
117
+ })
118
+
119
+ assay(context, 'returns a complete response when completion is declared', async (driver) => {
120
+ if (!hasCompletion(driver)) return
121
+ const result = await driver.complete(request())
122
+ expect(result.text.length).toBeGreaterThan(0)
123
+ expect(result.inputTokens).toBeGreaterThan(0)
124
+ expect(result.outputTokens).toBeGreaterThan(0)
125
+ })
126
+
127
+ assay(context, 'streams chunks in order and terminates exactly once', async (driver) => {
128
+ if (!hasCompletion(driver) || !hasStreaming(driver)) return
129
+ const expected = await driver.complete(request())
130
+ const chunks: CompletionChunk[] = []
131
+ for await (const chunk of driver.stream(request())) chunks.push(chunk)
132
+ const terminals = chunks.filter((chunk) => chunk.done)
133
+ const terminalIndex = chunks.findIndex((chunk) => chunk.done)
134
+ expect(chunks.length).toBeGreaterThan(1)
135
+ expect(terminals).toEqual([{ text: '', done: true }])
136
+ expect(terminalIndex).toBe(chunks.length - 1)
137
+ expect(
138
+ chunks
139
+ .filter((chunk) => !chunk.done)
140
+ .map((chunk) => chunk.text)
141
+ .join(''),
142
+ ).toBe(expected.text)
143
+ })
144
+
145
+ assay(
146
+ context,
147
+ 'returns stable embedding dimensions while preserving input order',
148
+ async (driver) => {
149
+ if (!hasEmbedding(driver)) return
150
+ const first = await driver.embed('assay-embedding-model', ['alpha', 'beta', 'gamma'])
151
+ const second = await driver.embed('assay-embedding-model', 'alpha')
152
+ expect(first.embeddings).toHaveLength(3)
153
+ const dimensions = first.embeddings.map((embedding) => embedding.length)
154
+ expect(new Set(dimensions).size).toBe(1)
155
+ expect(dimensions[0]).toBeGreaterThan(0)
156
+ expect(second.embeddings[0]?.length).toBe(dimensions[0])
157
+ expect(first.embeddings[0]).toEqual(second.embeddings[0])
158
+ },
159
+ )
160
+
161
+ assay(context, 'normalizes an empty embedding batch to Invalid', async (driver) => {
162
+ if (!hasEmbedding(driver)) return
163
+ const error = await expectInvalid(() => driver.embed('assay-embedding-model', []))
164
+ expect(error.metadata.fields?.input).toBeDefined()
165
+ })
166
+ })
167
+ }
@@ -0,0 +1,174 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import {
3
+ type CacheCapabilities,
4
+ type CacheDriver,
5
+ type LockCacheSurface,
6
+ type TaggedCacheSurface,
7
+ } from '@avelonjs/core'
8
+ import { assertCapabilitySurface, captureFailure, type SuiteContext } from '../harness'
9
+
10
+ type AssayDriver = CacheDriver<CacheCapabilities> & Partial<TaggedCacheSurface & LockCacheSurface>
11
+
12
+ function assay<TDriver extends AssayDriver>(
13
+ context: SuiteContext<TDriver>,
14
+ name: string,
15
+ assertion: (driver: TDriver) => Promise<void> | void,
16
+ ): void {
17
+ test(name, async () => {
18
+ const driver = await context.create()
19
+ try {
20
+ await assertion(driver)
21
+ } finally {
22
+ await context.cleanup?.(driver)
23
+ }
24
+ })
25
+ }
26
+
27
+ function hasTags(driver: AssayDriver): driver is AssayDriver & TaggedCacheSurface {
28
+ return driver.capabilities.tags && typeof driver.tags === 'function'
29
+ }
30
+
31
+ function hasLocks(driver: AssayDriver): driver is AssayDriver & LockCacheSurface {
32
+ return driver.capabilities.locks && typeof driver.lock === 'function'
33
+ }
34
+
35
+ /**
36
+ * Registers the portable cache contract conformance suite.
37
+ *
38
+ * A tagged namespace matches entries carrying every requested tag. Lock contention may reject the
39
+ * second acquirer immediately or block it until release, but may never run both callbacks together.
40
+ *
41
+ * @param context Fresh isolated cache drivers and optional cleanup.
42
+ */
43
+ export function cacheSuite<TDriver extends AssayDriver>(context: SuiteContext<TDriver>): void {
44
+ describe(`cache conformance: ${context.name}`, () => {
45
+ assay(context, 'reports a present and stable resolved cache instance', (driver) => {
46
+ const instance = driver.instance
47
+ expect(instance.length).toBeGreaterThan(0)
48
+ expect(driver.instance).toBe(instance)
49
+ })
50
+
51
+ assay(context, 'enforces every cache capability surface in both directions', (driver) => {
52
+ const surfaces: readonly [string, boolean, readonly string[]][] = [
53
+ ['tags', driver.capabilities.tags, ['tags']],
54
+ ['locks', driver.capabilities.locks, ['lock']],
55
+ ]
56
+ for (const [capability, declared, methods] of surfaces) {
57
+ assertCapabilitySurface(driver, capability, declared, methods)
58
+ const unavailable = { capabilities: { [capability]: false } }
59
+ assertCapabilitySurface(unavailable, capability, false, methods)
60
+ expect(() =>
61
+ assertCapabilitySurface(
62
+ { ...unavailable, [methods[0] ?? 'missing']: () => undefined },
63
+ capability,
64
+ false,
65
+ methods,
66
+ ),
67
+ ).toThrow()
68
+ expect(() =>
69
+ assertCapabilitySurface(
70
+ { capabilities: { [capability]: true } },
71
+ capability,
72
+ true,
73
+ methods,
74
+ ),
75
+ ).toThrow()
76
+ }
77
+ })
78
+
79
+ assay(context, 'returns null on a cache miss without throwing', async (driver) => {
80
+ await expect(driver.get('missing-key')).resolves.toBeNull()
81
+ })
82
+
83
+ assay(context, 'puts, gets, forgets, and flushes values', async (driver) => {
84
+ await driver.put('actor', { id: 'actor-1', roles: ['admin'] })
85
+ expect(await driver.get<{ id: string; roles: string[] }>('actor')).toEqual({
86
+ id: 'actor-1',
87
+ roles: ['admin'],
88
+ })
89
+ await driver.forget('actor')
90
+ expect(await driver.get('actor')).toBeNull()
91
+
92
+ await driver.put('first', 1)
93
+ await driver.put('second', 2)
94
+ await driver.flush()
95
+ expect(await driver.get('first')).toBeNull()
96
+ expect(await driver.get('second')).toBeNull()
97
+ })
98
+
99
+ assay(context, 'expires values after their TTL elapses', async (driver) => {
100
+ await driver.put('short-lived', 'value', 1)
101
+ expect(await driver.get<string>('short-lived')).toBe('value')
102
+ await new Promise((resolve) => setTimeout(resolve, 1_200))
103
+ expect(await driver.get('short-lived')).toBeNull()
104
+ })
105
+
106
+ assay(context, 'invalidates exactly the entries carrying a requested tag', async (driver) => {
107
+ if (!hasTags(driver)) return
108
+ await driver.put('untagged', 'keep')
109
+ await driver.tags(['accounts']).put('account', 'remove')
110
+ await driver.tags(['reports']).put('report', 'keep')
111
+ await driver.tags(['accounts', 'shared']).put('shared-account', 'remove')
112
+
113
+ await driver.tags(['accounts']).flush()
114
+ expect(await driver.get('account')).toBeNull()
115
+ expect(await driver.get('shared-account')).toBeNull()
116
+ expect(await driver.get<string>('untagged')).toBe('keep')
117
+ expect(await driver.get<string>('report')).toBe('keep')
118
+ })
119
+
120
+ assay(context, 'keeps lock callbacks exclusive and releases after success', async (driver) => {
121
+ if (!hasLocks(driver)) return
122
+ let signalStarted: (() => void) | undefined
123
+ let releaseFirst: (() => void) | undefined
124
+ const started = new Promise<void>((resolve) => {
125
+ signalStarted = resolve
126
+ })
127
+ const hold = new Promise<void>((resolve) => {
128
+ releaseFirst = resolve
129
+ })
130
+
131
+ const first = driver.lock('report-generation', 2, async () => {
132
+ signalStarted?.()
133
+ await hold
134
+ return 'first'
135
+ })
136
+ await started
137
+
138
+ let secondEntered = false
139
+ const second = driver
140
+ .lock('report-generation', 2, async () => {
141
+ secondEntered = true
142
+ return 'second'
143
+ })
144
+ .then(
145
+ (value) => ({ status: 'acquired' as const, value }),
146
+ () => ({ status: 'rejected' as const }),
147
+ )
148
+
149
+ await new Promise((resolve) => setTimeout(resolve, 30))
150
+ expect(secondEntered).toBe(false)
151
+ releaseFirst?.()
152
+ expect(await first).toBe('first')
153
+ const outcome = await second
154
+ expect(['acquired', 'rejected']).toContain(outcome.status)
155
+
156
+ expect(await driver.lock('report-generation', 2, async () => 'after-release')).toBe(
157
+ 'after-release',
158
+ )
159
+ })
160
+
161
+ assay(context, 'releases a lock when its callback throws', async (driver) => {
162
+ if (!hasLocks(driver)) return
163
+ const failure = new Error('callback failed')
164
+ expect(
165
+ await captureFailure(() =>
166
+ driver.lock('failing-lock', 2, async () => {
167
+ throw failure
168
+ }),
169
+ ),
170
+ ).toBe(failure)
171
+ expect(await driver.lock('failing-lock', 2, async () => 'recovered')).toBe('recovered')
172
+ })
173
+ })
174
+ }