|
6 | 6 | * for parsing and our deparser for converting back to CEL strings. |
7 | 7 | * |
8 | 8 | * Run with: npx ts-node --esm scripts/test-roundtrip.ts |
| 9 | + * Or with a fixture file: npx ts-node --esm scripts/test-roundtrip.ts __fixtures__/expressions/basic.txt |
9 | 10 | */ |
10 | 11 |
|
11 | 12 | import { parse } from '@marcbachmann/cel-js'; |
12 | 13 | import { deparse, Expr } from '../src/deparser'; |
13 | 14 | import { convertToProtoExpr, MarcAstNode } from '../src/converter'; |
| 15 | +import { readFileSync } from 'fs'; |
| 16 | +import { resolve, dirname } from 'path'; |
| 17 | +import { fileURLToPath } from 'url'; |
| 18 | + |
| 19 | +const __filename = fileURLToPath(import.meta.url); |
| 20 | +const __dirname = dirname(__filename); |
14 | 21 |
|
15 | 22 | interface TestResult { |
16 | 23 | expression: string; |
@@ -51,90 +58,114 @@ function testRoundTrip(expression: string): TestResult { |
51 | 58 | } |
52 | 59 | } |
53 | 60 |
|
54 | | -const testCases = [ |
55 | | - // Literals |
56 | | - '1', |
57 | | - '42', |
58 | | - '3.14', |
59 | | - 'true', |
60 | | - 'false', |
61 | | - 'null', |
62 | | - '"hello"', |
63 | | - '"hello world"', |
64 | | - |
65 | | - // Unsigned integers |
66 | | - '1u', |
67 | | - '42u', |
68 | | - |
69 | | - // Identifiers |
70 | | - 'x', |
71 | | - 'foo', |
72 | | - 'request', |
73 | | - |
74 | | - // Field access |
75 | | - 'request.auth', |
76 | | - 'request.auth.claims', |
77 | | - 'request.auth.claims.email', |
78 | | - |
79 | | - // Arithmetic operators |
80 | | - '1 + 2', |
81 | | - 'a + b', |
82 | | - 'x - y', |
83 | | - 'a * b', |
84 | | - 'x / y', |
85 | | - '1 + 2 * 3', |
86 | | - |
87 | | - // Comparison operators |
88 | | - 'a == b', |
89 | | - 'x != y', |
90 | | - 'a < b', |
91 | | - 'a <= b', |
92 | | - 'a > b', |
93 | | - 'a >= b', |
94 | | - |
95 | | - // Logical operators |
96 | | - 'a && b', |
97 | | - 'x || y', |
98 | | - '!a', |
99 | | - 'a && b && c', |
100 | | - 'a || b || c', |
101 | | - |
102 | | - // Ternary conditional |
103 | | - 'x ? 1 : 2', |
104 | | - 'a == b ? "yes" : "no"', |
105 | | - |
106 | | - // List literals |
107 | | - '[]', |
108 | | - '[1]', |
109 | | - '[1, 2, 3]', |
110 | | - |
111 | | - // Map literals |
112 | | - '{}', |
113 | | - '{"a": 1}', |
114 | | - '{"a": 1, "b": 2}', |
115 | | - |
116 | | - // Index access |
117 | | - 'list[0]', |
118 | | - 'map["key"]', |
119 | | - |
120 | | - // Function calls |
121 | | - 'size(list)', |
122 | | - 'int(x)', |
123 | | - 'string(42)', |
124 | | - |
125 | | - // Method calls |
126 | | - 'list.size()', |
127 | | - 'str.contains("test")', |
128 | | - |
129 | | - // In operator |
130 | | - 'x in list', |
131 | | - '"admin" in roles', |
132 | | - |
133 | | - // Complex expressions |
134 | | - 'request.auth.claims.email == "admin@example.com"', |
135 | | - 'size(request.body) > 0 && request.method == "POST"', |
136 | | - 'user.age >= 18 && "admin" in user.roles' |
137 | | -]; |
| 61 | +/** |
| 62 | + * Load CEL expressions from a fixture file |
| 63 | + * Lines starting with # are comments, empty lines are ignored |
| 64 | + */ |
| 65 | +function loadFixtures(filePath: string): string[] { |
| 66 | + const content = readFileSync(filePath, 'utf-8'); |
| 67 | + return content |
| 68 | + .split('\n') |
| 69 | + .map((line) => line.trim()) |
| 70 | + .filter((line) => line && !line.startsWith('#')); |
| 71 | +} |
| 72 | + |
| 73 | +// Check if a fixture file was provided as argument |
| 74 | +const fixtureArg = process.argv[2]; |
| 75 | +let testCases: string[]; |
| 76 | + |
| 77 | +if (fixtureArg) { |
| 78 | + // Load from fixture file |
| 79 | + const fixturePath = resolve(__dirname, '..', fixtureArg); |
| 80 | + console.log(`Loading fixtures from: ${fixturePath}\n`); |
| 81 | + testCases = loadFixtures(fixturePath); |
| 82 | +} else { |
| 83 | + // Use default test cases |
| 84 | + testCases = [ |
| 85 | + // Literals |
| 86 | + '1', |
| 87 | + '42', |
| 88 | + '3.14', |
| 89 | + 'true', |
| 90 | + 'false', |
| 91 | + 'null', |
| 92 | + '"hello"', |
| 93 | + '"hello world"', |
| 94 | + |
| 95 | + // Unsigned integers |
| 96 | + '1u', |
| 97 | + '42u', |
| 98 | + |
| 99 | + // Identifiers |
| 100 | + 'x', |
| 101 | + 'foo', |
| 102 | + 'request', |
| 103 | + |
| 104 | + // Field access |
| 105 | + 'request.auth', |
| 106 | + 'request.auth.claims', |
| 107 | + 'request.auth.claims.email', |
| 108 | + |
| 109 | + // Arithmetic operators |
| 110 | + '1 + 2', |
| 111 | + 'a + b', |
| 112 | + 'x - y', |
| 113 | + 'a * b', |
| 114 | + 'x / y', |
| 115 | + '1 + 2 * 3', |
| 116 | + |
| 117 | + // Comparison operators |
| 118 | + 'a == b', |
| 119 | + 'x != y', |
| 120 | + 'a < b', |
| 121 | + 'a <= b', |
| 122 | + 'a > b', |
| 123 | + 'a >= b', |
| 124 | + |
| 125 | + // Logical operators |
| 126 | + 'a && b', |
| 127 | + 'x || y', |
| 128 | + '!a', |
| 129 | + 'a && b && c', |
| 130 | + 'a || b || c', |
| 131 | + |
| 132 | + // Ternary conditional |
| 133 | + 'x ? 1 : 2', |
| 134 | + 'a == b ? "yes" : "no"', |
| 135 | + |
| 136 | + // List literals |
| 137 | + '[]', |
| 138 | + '[1]', |
| 139 | + '[1, 2, 3]', |
| 140 | + |
| 141 | + // Map literals |
| 142 | + '{}', |
| 143 | + '{"a": 1}', |
| 144 | + '{"a": 1, "b": 2}', |
| 145 | + |
| 146 | + // Index access |
| 147 | + 'list[0]', |
| 148 | + 'map["key"]', |
| 149 | + |
| 150 | + // Function calls |
| 151 | + 'size(list)', |
| 152 | + 'int(x)', |
| 153 | + 'string(42)', |
| 154 | + |
| 155 | + // Method calls |
| 156 | + 'list.size()', |
| 157 | + 'str.contains("test")', |
| 158 | + |
| 159 | + // In operator |
| 160 | + 'x in list', |
| 161 | + '"admin" in roles', |
| 162 | + |
| 163 | + // Complex expressions |
| 164 | + 'request.auth.claims.email == "admin@example.com"', |
| 165 | + 'size(request.body) > 0 && request.method == "POST"', |
| 166 | + 'user.age >= 18 && "admin" in user.roles' |
| 167 | + ]; |
| 168 | +} |
138 | 169 |
|
139 | 170 | console.log('CEL Round-Trip Tests'); |
140 | 171 | console.log('====================\n'); |
|
0 commit comments