@figulus/schema 0.4.0-alpha-dev

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.
Files changed (50) hide show
  1. package/dist/figspec.d.ts +4394 -0
  2. package/dist/figspec.d.ts.map +1 -0
  3. package/dist/figspec.js +214 -0
  4. package/dist/figspec.js.map +1 -0
  5. package/dist/figstack.d.ts +419 -0
  6. package/dist/figstack.d.ts.map +1 -0
  7. package/dist/figstack.js +72 -0
  8. package/dist/figstack.js.map +1 -0
  9. package/dist/health.d.ts +16 -0
  10. package/dist/health.d.ts.map +1 -0
  11. package/dist/health.js +7 -0
  12. package/dist/health.js.map +1 -0
  13. package/dist/image.d.ts +115 -0
  14. package/dist/image.d.ts.map +1 -0
  15. package/dist/image.js +42 -0
  16. package/dist/image.js.map +1 -0
  17. package/dist/index.d.ts +9 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +9 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/run-request.d.ts +244 -0
  22. package/dist/run-request.d.ts.map +1 -0
  23. package/dist/run-request.js +43 -0
  24. package/dist/run-request.js.map +1 -0
  25. package/dist/session.d.ts +447 -0
  26. package/dist/session.d.ts.map +1 -0
  27. package/dist/session.js +78 -0
  28. package/dist/session.js.map +1 -0
  29. package/dist/shared.d.ts +90 -0
  30. package/dist/shared.d.ts.map +1 -0
  31. package/dist/shared.js +41 -0
  32. package/dist/shared.js.map +1 -0
  33. package/dist/volume.d.ts +140 -0
  34. package/dist/volume.d.ts.map +1 -0
  35. package/dist/volume.js +37 -0
  36. package/dist/volume.js.map +1 -0
  37. package/package.json +22 -0
  38. package/src/figspec.ts +279 -0
  39. package/src/figstack.ts +92 -0
  40. package/src/health.ts +8 -0
  41. package/src/image.ts +55 -0
  42. package/src/index.ts +8 -0
  43. package/src/run-request.ts +55 -0
  44. package/src/session.ts +101 -0
  45. package/src/shared.ts +56 -0
  46. package/src/volume.ts +50 -0
  47. package/tests/figspec.test.ts +383 -0
  48. package/tests/schemas.test.ts +187 -0
  49. package/tsconfig.build.json +11 -0
  50. package/tsconfig.json +12 -0
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Zod Schema Validation Tests
3
+ *
4
+ * Tests that Zod schemas correctly validate and reject data.
5
+ * Focuses on the most commonly used request schemas: RunRequest,
6
+ * CreateSessionRequest, and SyncFileRequest.
7
+ */
8
+
9
+ import { describe, it, expect } from 'vitest';
10
+ import {
11
+ RunRequestSchema,
12
+ JobStatusSchema,
13
+ CreateSessionRequestSchema,
14
+ SyncFileRequestSchema,
15
+ } from '../src/index.js';
16
+
17
+ describe('Zod Schema Validation', () => {
18
+ describe('RunRequest Schema', () => {
19
+ it('should validate a minimal valid request', () => {
20
+ const valid = { job_id: 'job-001', image: 'node:20-alpine' };
21
+ expect(() => RunRequestSchema.parse(valid)).not.toThrow();
22
+ });
23
+
24
+ it('should validate a fully populated request', () => {
25
+ const valid = {
26
+ job_id: 'job-002',
27
+ image: 'node:20-alpine',
28
+ command: ['node', 'index.js'],
29
+ environment: { NODE_ENV: 'test' },
30
+ volumes: [{ host_path: '/tmp/src', container_path: '/app', read_only: false }],
31
+ named_volumes: [{ volume_name: 'cache', container_path: '/root/.npm', read_only: false }],
32
+ resources: { memory_limit: '512M', cpu_limit: '1' },
33
+ timeout: 300,
34
+ working_dir: '/app',
35
+ network_mode: 'bridge',
36
+ ports: [{ container: 3000, host: 8080 }],
37
+ };
38
+ expect(() => RunRequestSchema.parse(valid)).not.toThrow();
39
+ });
40
+
41
+ it('should reject missing job_id', () => {
42
+ const invalid = { image: 'node:20-alpine' };
43
+ expect(() => RunRequestSchema.parse(invalid)).toThrow();
44
+ });
45
+
46
+ it('should reject missing image', () => {
47
+ const invalid = { job_id: 'job-001' };
48
+ expect(() => RunRequestSchema.parse(invalid)).toThrow();
49
+ });
50
+
51
+ it('should reject empty job_id', () => {
52
+ const invalid = { job_id: '', image: 'node:20-alpine' };
53
+ expect(() => RunRequestSchema.parse(invalid)).toThrow();
54
+ });
55
+
56
+ it('should reject negative timeout', () => {
57
+ const invalid = { job_id: 'job-001', image: 'node:20', timeout: -1 };
58
+ expect(() => RunRequestSchema.parse(invalid)).toThrow();
59
+ });
60
+
61
+ it('should reject timeout exceeding max', () => {
62
+ const invalid = { job_id: 'job-001', image: 'node:20', timeout: 99999 };
63
+ expect(() => RunRequestSchema.parse(invalid)).toThrow();
64
+ });
65
+
66
+ it('should reject non-integer timeout', () => {
67
+ const invalid = { job_id: 'job-001', image: 'node:20', timeout: 30.5 };
68
+ expect(() => RunRequestSchema.parse(invalid)).toThrow();
69
+ });
70
+
71
+ it('should reject invalid port number', () => {
72
+ const invalid = {
73
+ job_id: 'job-001',
74
+ image: 'node:20',
75
+ ports: [{ container: 99999 }],
76
+ };
77
+ expect(() => RunRequestSchema.parse(invalid)).toThrow();
78
+ });
79
+ });
80
+
81
+ describe('JobStatus Schema', () => {
82
+ it('should accept all valid status values', () => {
83
+ const validValues = ['pending', 'running', 'completed', 'failed', 'timeout', 'awaiting_cleanup'];
84
+ for (const val of validValues) {
85
+ expect(() => JobStatusSchema.parse(val)).not.toThrow();
86
+ }
87
+ });
88
+
89
+ it('should reject invalid status values', () => {
90
+ expect(() => JobStatusSchema.parse('cancelled')).toThrow();
91
+ expect(() => JobStatusSchema.parse('done')).toThrow();
92
+ expect(() => JobStatusSchema.parse('')).toThrow();
93
+ });
94
+ });
95
+
96
+ describe('CreateSessionRequest Schema', () => {
97
+ it('should validate a minimal valid request', () => {
98
+ const valid = { image_tag: 'localhost:5000/figulus:sha256-abc123' };
99
+ expect(() => CreateSessionRequestSchema.parse(valid)).not.toThrow();
100
+ });
101
+
102
+ it('should validate a request with config', () => {
103
+ const valid = {
104
+ image_tag: 'localhost:5000/figulus:sha256-abc123',
105
+ ttl: 600,
106
+ config: {
107
+ working_dir: '/app',
108
+ environment: { NODE_ENV: 'development' },
109
+ init_cmd: ['npm', 'install'],
110
+ ports: [{ container: 3000 }],
111
+ },
112
+ };
113
+ expect(() => CreateSessionRequestSchema.parse(valid)).not.toThrow();
114
+ });
115
+
116
+ it('should reject missing image_tag', () => {
117
+ const invalid = { ttl: 600 };
118
+ expect(() => CreateSessionRequestSchema.parse(invalid)).toThrow();
119
+ });
120
+
121
+ it('should reject empty image_tag', () => {
122
+ const invalid = { image_tag: '' };
123
+ expect(() => CreateSessionRequestSchema.parse(invalid)).toThrow();
124
+ });
125
+
126
+ it('should reject negative ttl', () => {
127
+ const invalid = { image_tag: 'some-image:latest', ttl: -1 };
128
+ expect(() => CreateSessionRequestSchema.parse(invalid)).toThrow();
129
+ });
130
+
131
+ it('should reject non-integer ttl', () => {
132
+ const invalid = { image_tag: 'some-image:latest', ttl: 60.5 };
133
+ expect(() => CreateSessionRequestSchema.parse(invalid)).toThrow();
134
+ });
135
+ });
136
+
137
+ describe('SyncFileRequest Schema', () => {
138
+ it('should validate a valid base64 request', () => {
139
+ const valid = {
140
+ targetPath: '/app/src/index.ts',
141
+ content: 'Y29uc29sZS5sb2coImhlbGxvIik=',
142
+ encoding: 'base64',
143
+ };
144
+ expect(() => SyncFileRequestSchema.parse(valid)).not.toThrow();
145
+ });
146
+
147
+ it('should validate a valid utf8 request', () => {
148
+ const valid = {
149
+ targetPath: '/app/src/index.ts',
150
+ content: 'console.log("hello")',
151
+ encoding: 'utf8',
152
+ };
153
+ expect(() => SyncFileRequestSchema.parse(valid)).not.toThrow();
154
+ });
155
+
156
+ it('should default encoding to base64 when omitted', () => {
157
+ const input = { targetPath: '/app/src/index.ts', content: 'abc' };
158
+ const result = SyncFileRequestSchema.parse(input);
159
+ expect(result.encoding).toBe('base64');
160
+ });
161
+
162
+ it('should reject missing targetPath', () => {
163
+ const invalid = { content: 'abc', encoding: 'base64' };
164
+ expect(() => SyncFileRequestSchema.parse(invalid)).toThrow();
165
+ });
166
+
167
+ it('should reject empty targetPath', () => {
168
+ const invalid = { targetPath: '', content: 'abc', encoding: 'base64' };
169
+ expect(() => SyncFileRequestSchema.parse(invalid)).toThrow();
170
+ });
171
+
172
+ it('should reject missing content', () => {
173
+ const invalid = { targetPath: '/app/file.ts', encoding: 'base64' };
174
+ expect(() => SyncFileRequestSchema.parse(invalid)).toThrow();
175
+ });
176
+
177
+ it('should reject empty content', () => {
178
+ const invalid = { targetPath: '/app/file.ts', content: '', encoding: 'base64' };
179
+ expect(() => SyncFileRequestSchema.parse(invalid)).toThrow();
180
+ });
181
+
182
+ it('should reject invalid encoding value', () => {
183
+ const invalid = { targetPath: '/app/file.ts', content: 'abc', encoding: 'hex' };
184
+ expect(() => SyncFileRequestSchema.parse(invalid)).toThrow();
185
+ });
186
+ });
187
+ });
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": false,
5
+ "outDir": "./dist",
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "sourceMap": true
9
+ },
10
+ "exclude": ["tests/**/*", "node_modules"]
11
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ES2022",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "noEmit": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true
10
+ },
11
+ "include": ["src/**/*.ts", "tests/**/*.ts"]
12
+ }