Skip to content

Commit 3cc3000

Browse files
Copilothotlong
andcommitted
Add Hub API contracts and enhanced protocols (federation, security)
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent a371226 commit 3cc3000

8 files changed

Lines changed: 3053 additions & 0 deletions

File tree

packages/spec/src/api/hub.test.ts

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
import { describe, it, expect } from 'vitest';
2+
import {
3+
CreateSpaceRequestSchema,
4+
UpdateSpaceRequestSchema,
5+
SpaceResponseSchema,
6+
ListSpacesResponseSchema,
7+
PublishPluginRequestSchema,
8+
SearchPluginsResponseSchema,
9+
IssueLicenseRequestSchema,
10+
ValidateLicenseResponseSchema,
11+
CompileManifestRequestSchema,
12+
HubHealthResponseSchema,
13+
HubMetricsResponseSchema,
14+
} from './hub.zod';
15+
16+
describe('Hub API Protocol', () => {
17+
describe('Space Management', () => {
18+
it('should validate CreateSpaceRequest', () => {
19+
const validRequest = {
20+
name: 'My Workspace',
21+
slug: 'my-workspace',
22+
ownerId: 'user_123',
23+
runtime: {
24+
isolation: 'shared_schema' as const,
25+
quotas: {
26+
maxUsers: 50,
27+
maxStorage: 107374182400, // 100GB
28+
apiRateLimit: 10000,
29+
},
30+
},
31+
};
32+
33+
const result = CreateSpaceRequestSchema.safeParse(validRequest);
34+
expect(result.success).toBe(true);
35+
});
36+
37+
it('should reject invalid slug format', () => {
38+
const invalidRequest = {
39+
name: 'My Workspace',
40+
slug: 'My_Invalid_Slug!',
41+
ownerId: 'user_123',
42+
};
43+
44+
const result = CreateSpaceRequestSchema.safeParse(invalidRequest);
45+
expect(result.success).toBe(false);
46+
});
47+
48+
it('should validate UpdateSpaceRequest with partial data', () => {
49+
const validUpdate = {
50+
name: 'Updated Name',
51+
};
52+
53+
const result = UpdateSpaceRequestSchema.safeParse(validUpdate);
54+
expect(result.success).toBe(true);
55+
});
56+
57+
it('should validate SpaceResponse', () => {
58+
const validResponse = {
59+
id: '550e8400-e29b-41d4-a716-446655440000',
60+
name: 'Sales Team',
61+
slug: 'sales-team',
62+
ownerId: 'user_123',
63+
bom: {
64+
tenantId: 'tenant_123',
65+
dependencies: [],
66+
resolutionStrategy: 'override' as const,
67+
},
68+
createdAt: '2024-01-01T00:00:00Z',
69+
updatedAt: '2024-01-02T00:00:00Z',
70+
};
71+
72+
const result = SpaceResponseSchema.safeParse(validResponse);
73+
expect(result.success).toBe(true);
74+
});
75+
76+
it('should validate ListSpacesResponse', () => {
77+
const validResponse = {
78+
data: [
79+
{
80+
id: '550e8400-e29b-41d4-a716-446655440000',
81+
name: 'Sales Team',
82+
slug: 'sales-team',
83+
ownerId: 'user_123',
84+
bom: {
85+
tenantId: 'tenant_123',
86+
dependencies: [],
87+
resolutionStrategy: 'override' as const,
88+
},
89+
createdAt: '2024-01-01T00:00:00Z',
90+
updatedAt: '2024-01-02T00:00:00Z',
91+
},
92+
],
93+
pagination: {
94+
page: 1,
95+
perPage: 20,
96+
total: 1,
97+
totalPages: 1,
98+
hasNext: false,
99+
hasPrev: false,
100+
},
101+
};
102+
103+
const result = ListSpacesResponseSchema.safeParse(validResponse);
104+
expect(result.success).toBe(true);
105+
});
106+
});
107+
108+
describe('Plugin Registry', () => {
109+
it('should validate PublishPluginRequest', () => {
110+
const validRequest = {
111+
id: 'com.acme.crm',
112+
version: '1.0.0',
113+
name: 'Advanced CRM',
114+
description: 'Enterprise CRM solution',
115+
vendor: {
116+
id: 'com.acme',
117+
name: 'Acme Corporation',
118+
verified: true,
119+
trustLevel: 'verified' as const,
120+
},
121+
};
122+
123+
const result = PublishPluginRequestSchema.safeParse(validRequest);
124+
expect(result.success).toBe(true);
125+
});
126+
127+
it('should validate SearchPluginsResponse', () => {
128+
const validResponse = {
129+
data: [
130+
{
131+
id: 'com.acme.crm',
132+
version: '1.0.0',
133+
name: 'Advanced CRM',
134+
vendor: {
135+
id: 'com.acme',
136+
name: 'Acme Corporation',
137+
verified: true,
138+
trustLevel: 'verified' as const,
139+
},
140+
deprecated: false,
141+
},
142+
],
143+
pagination: {
144+
page: 1,
145+
perPage: 20,
146+
total: 1,
147+
totalPages: 1,
148+
hasNext: false,
149+
hasPrev: false,
150+
},
151+
};
152+
153+
const result = SearchPluginsResponseSchema.safeParse(validResponse);
154+
expect(result.success).toBe(true);
155+
});
156+
});
157+
158+
describe('License Management', () => {
159+
it('should validate IssueLicenseRequest', () => {
160+
const validRequest = {
161+
spaceId: '550e8400-e29b-41d4-a716-446655440000',
162+
planCode: 'enterprise_v1',
163+
expiresAt: '2025-12-31T23:59:59Z',
164+
customFeatures: ['advanced_analytics'],
165+
customLimits: {
166+
storage_gb: 500,
167+
},
168+
plugins: ['com.acme.crm'],
169+
};
170+
171+
const result = IssueLicenseRequestSchema.safeParse(validRequest);
172+
expect(result.success).toBe(true);
173+
});
174+
175+
it('should validate ValidateLicenseResponse', () => {
176+
const validResponse = {
177+
valid: true,
178+
license: {
179+
spaceId: '550e8400-e29b-41d4-a716-446655440000',
180+
planCode: 'enterprise_v1',
181+
status: 'active' as const,
182+
issuedAt: '2024-01-01T00:00:00Z',
183+
},
184+
errors: [],
185+
warnings: [],
186+
};
187+
188+
const result = ValidateLicenseResponseSchema.safeParse(validResponse);
189+
expect(result.success).toBe(true);
190+
});
191+
192+
it('should validate failed license validation', () => {
193+
const invalidResponse = {
194+
valid: false,
195+
errors: ['License has expired', 'Invalid signature'],
196+
warnings: [],
197+
};
198+
199+
const result = ValidateLicenseResponseSchema.safeParse(invalidResponse);
200+
expect(result.success).toBe(true);
201+
if (result.success) {
202+
expect(result.data.valid).toBe(false);
203+
expect(result.data.errors).toHaveLength(2);
204+
}
205+
});
206+
});
207+
208+
describe('Composer Service', () => {
209+
it('should validate CompileManifestRequest', () => {
210+
const validRequest = {
211+
bom: {
212+
tenantId: 'tenant_123',
213+
dependencies: [
214+
{
215+
id: 'com.objectstack.crm',
216+
version: '2.0.0',
217+
configuration: {
218+
currency: 'USD',
219+
},
220+
},
221+
],
222+
resolutionStrategy: 'override' as const,
223+
},
224+
runtimeVersion: '1.5.0',
225+
dryRun: false,
226+
};
227+
228+
const result = CompileManifestRequestSchema.safeParse(validRequest);
229+
expect(result.success).toBe(true);
230+
});
231+
});
232+
233+
describe('Health & Monitoring', () => {
234+
it('should validate HubHealthResponse', () => {
235+
const validResponse = {
236+
status: 'healthy' as const,
237+
version: '1.0.0',
238+
uptime: 86400,
239+
services: {
240+
database: {
241+
status: 'healthy' as const,
242+
latency: 5,
243+
},
244+
cache: {
245+
status: 'healthy' as const,
246+
latency: 2,
247+
},
248+
},
249+
timestamp: '2024-01-01T12:00:00Z',
250+
};
251+
252+
const result = HubHealthResponseSchema.safeParse(validResponse);
253+
expect(result.success).toBe(true);
254+
});
255+
256+
it('should validate HubMetricsResponse', () => {
257+
const validResponse = {
258+
metrics: {
259+
spaces: {
260+
total: 1250,
261+
active: 980,
262+
created_last_30d: 45,
263+
},
264+
tenants: {
265+
total: 320,
266+
active: 285,
267+
},
268+
plugins: {
269+
total: 156,
270+
published_last_30d: 8,
271+
total_downloads: 456789,
272+
},
273+
api: {
274+
requests_per_minute: 850,
275+
avg_response_time: 125,
276+
error_rate: 0.002,
277+
},
278+
},
279+
timestamp: '2024-01-01T12:00:00Z',
280+
};
281+
282+
const result = HubMetricsResponseSchema.safeParse(validResponse);
283+
expect(result.success).toBe(true);
284+
});
285+
});
286+
});

0 commit comments

Comments
 (0)