Skip to content

Commit 5765077

Browse files
Copilothotlong
andcommitted
Add test files for api/metadata, api/auth, api/analytics, and api/storage schemas
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent b46e3ca commit 5765077

4 files changed

Lines changed: 929 additions & 0 deletions

File tree

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
import { describe, it, expect } from 'vitest';
2+
import {
3+
AnalyticsEndpoint,
4+
AnalyticsQueryRequestSchema,
5+
AnalyticsResultResponseSchema,
6+
GetAnalyticsMetaRequestSchema,
7+
AnalyticsMetadataResponseSchema,
8+
AnalyticsSqlResponseSchema,
9+
} from './analytics.zod';
10+
11+
describe('AnalyticsEndpoint', () => {
12+
it('should accept all valid endpoints', () => {
13+
for (const ep of [
14+
'/api/v1/analytics/query',
15+
'/api/v1/analytics/meta',
16+
'/api/v1/analytics/sql',
17+
]) {
18+
expect(AnalyticsEndpoint.parse(ep)).toBe(ep);
19+
}
20+
});
21+
22+
it('should reject invalid endpoint', () => {
23+
expect(() => AnalyticsEndpoint.parse('/api/v1/analytics/unknown')).toThrow();
24+
});
25+
});
26+
27+
describe('AnalyticsQueryRequestSchema', () => {
28+
it('should accept valid query request with defaults', () => {
29+
const req = AnalyticsQueryRequestSchema.parse({
30+
query: {
31+
measures: ['total_revenue'],
32+
},
33+
cube: 'orders',
34+
});
35+
expect(req.cube).toBe('orders');
36+
expect(req.format).toBe('json');
37+
expect(req.query.measures).toEqual(['total_revenue']);
38+
});
39+
40+
it('should accept query with explicit format', () => {
41+
const req = AnalyticsQueryRequestSchema.parse({
42+
query: {
43+
measures: ['count'],
44+
dimensions: ['category'],
45+
},
46+
cube: 'products',
47+
format: 'csv',
48+
});
49+
expect(req.format).toBe('csv');
50+
});
51+
52+
it('should accept query with filters and time dimensions', () => {
53+
const req = AnalyticsQueryRequestSchema.parse({
54+
query: {
55+
measures: ['total_revenue'],
56+
dimensions: ['product_category'],
57+
filters: [
58+
{ member: 'status', operator: 'equals', values: ['active'] },
59+
],
60+
timeDimensions: [
61+
{ dimension: 'created_at', granularity: 'month', dateRange: 'Last 7 days' },
62+
],
63+
order: { total_revenue: 'desc' },
64+
limit: 100,
65+
},
66+
cube: 'sales',
67+
format: 'xlsx',
68+
});
69+
expect(req.query.filters).toHaveLength(1);
70+
expect(req.query.timeDimensions).toHaveLength(1);
71+
});
72+
73+
it('should reject missing cube', () => {
74+
expect(() =>
75+
AnalyticsQueryRequestSchema.parse({
76+
query: { measures: ['x'] },
77+
})
78+
).toThrow();
79+
});
80+
81+
it('should reject missing query', () => {
82+
expect(() =>
83+
AnalyticsQueryRequestSchema.parse({ cube: 'test' })
84+
).toThrow();
85+
});
86+
87+
it('should reject invalid format', () => {
88+
expect(() =>
89+
AnalyticsQueryRequestSchema.parse({
90+
query: { measures: ['x'] },
91+
cube: 'test',
92+
format: 'xml',
93+
})
94+
).toThrow();
95+
});
96+
});
97+
98+
describe('AnalyticsResultResponseSchema', () => {
99+
it('should accept valid result response', () => {
100+
const resp = AnalyticsResultResponseSchema.parse({
101+
success: true,
102+
data: {
103+
rows: [
104+
{ category: 'Electronics', total: 5000 },
105+
{ category: 'Books', total: 1200 },
106+
],
107+
fields: [
108+
{ name: 'category', type: 'string' },
109+
{ name: 'total', type: 'number' },
110+
],
111+
},
112+
});
113+
expect(resp.data.rows).toHaveLength(2);
114+
expect(resp.data.fields).toHaveLength(2);
115+
expect(resp.data.sql).toBeUndefined();
116+
});
117+
118+
it('should accept result with sql debug info', () => {
119+
const resp = AnalyticsResultResponseSchema.parse({
120+
success: true,
121+
data: {
122+
rows: [],
123+
fields: [],
124+
sql: 'SELECT category, SUM(amount) FROM orders GROUP BY category',
125+
},
126+
});
127+
expect(resp.data.sql).toBeDefined();
128+
});
129+
130+
it('should reject missing rows or fields', () => {
131+
expect(() =>
132+
AnalyticsResultResponseSchema.parse({
133+
success: true,
134+
data: { rows: [] },
135+
})
136+
).toThrow();
137+
138+
expect(() =>
139+
AnalyticsResultResponseSchema.parse({
140+
success: true,
141+
data: { fields: [] },
142+
})
143+
).toThrow();
144+
});
145+
});
146+
147+
describe('GetAnalyticsMetaRequestSchema', () => {
148+
it('should accept empty request', () => {
149+
const req = GetAnalyticsMetaRequestSchema.parse({});
150+
expect(req.cube).toBeUndefined();
151+
});
152+
153+
it('should accept request with cube filter', () => {
154+
const req = GetAnalyticsMetaRequestSchema.parse({ cube: 'orders' });
155+
expect(req.cube).toBe('orders');
156+
});
157+
});
158+
159+
describe('AnalyticsMetadataResponseSchema', () => {
160+
it('should accept valid metadata response', () => {
161+
const resp = AnalyticsMetadataResponseSchema.parse({
162+
success: true,
163+
data: {
164+
cubes: [
165+
{
166+
name: 'orders',
167+
sql: 'SELECT * FROM orders',
168+
measures: {
169+
total_revenue: {
170+
name: 'total_revenue',
171+
label: 'Total Revenue',
172+
type: 'sum',
173+
sql: 'amount',
174+
},
175+
},
176+
dimensions: {
177+
status: {
178+
name: 'status',
179+
label: 'Status',
180+
type: 'string',
181+
sql: 'status',
182+
},
183+
},
184+
},
185+
],
186+
},
187+
});
188+
expect(resp.data.cubes).toHaveLength(1);
189+
expect(resp.data.cubes[0].name).toBe('orders');
190+
});
191+
192+
it('should accept empty cubes list', () => {
193+
const resp = AnalyticsMetadataResponseSchema.parse({
194+
success: true,
195+
data: { cubes: [] },
196+
});
197+
expect(resp.data.cubes).toHaveLength(0);
198+
});
199+
200+
it('should reject missing cubes', () => {
201+
expect(() =>
202+
AnalyticsMetadataResponseSchema.parse({
203+
success: true,
204+
data: {},
205+
})
206+
).toThrow();
207+
});
208+
});
209+
210+
describe('AnalyticsSqlResponseSchema', () => {
211+
it('should accept valid SQL response', () => {
212+
const resp = AnalyticsSqlResponseSchema.parse({
213+
success: true,
214+
data: {
215+
sql: 'SELECT COUNT(*) FROM orders WHERE status = $1',
216+
params: ['active'],
217+
},
218+
});
219+
expect(resp.data.sql).toContain('SELECT');
220+
expect(resp.data.params).toEqual(['active']);
221+
});
222+
223+
it('should accept empty params', () => {
224+
const resp = AnalyticsSqlResponseSchema.parse({
225+
success: true,
226+
data: {
227+
sql: 'SELECT 1',
228+
params: [],
229+
},
230+
});
231+
expect(resp.data.params).toHaveLength(0);
232+
});
233+
234+
it('should reject missing sql or params', () => {
235+
expect(() =>
236+
AnalyticsSqlResponseSchema.parse({
237+
success: true,
238+
data: { params: [] },
239+
})
240+
).toThrow();
241+
242+
expect(() =>
243+
AnalyticsSqlResponseSchema.parse({
244+
success: true,
245+
data: { sql: 'SELECT 1' },
246+
})
247+
).toThrow();
248+
});
249+
});

0 commit comments

Comments
 (0)