| 1222 | } |
| 1223 | |
| 1224 | func TestNodeBudget(t *testing.T) { |
| 1225 | tests := []struct { |
| 1226 | name string |
| 1227 | expr string |
| 1228 | maxNodes uint |
| 1229 | shouldError bool |
| 1230 | }{ |
| 1231 | { |
| 1232 | name: "simple expression equal to limit", |
| 1233 | expr: "a + b", |
| 1234 | maxNodes: 3, |
| 1235 | shouldError: false, |
| 1236 | }, |
| 1237 | { |
| 1238 | name: "medium expression under limit", |
| 1239 | expr: "a + b * c / d", |
| 1240 | maxNodes: 20, |
| 1241 | shouldError: false, |
| 1242 | }, |
| 1243 | { |
| 1244 | name: "deeply nested expression over limit", |
| 1245 | expr: "1 + (2 + (3 + (4 + (5 + (6 + (7 + 8))))))", |
| 1246 | maxNodes: 10, |
| 1247 | shouldError: true, |
| 1248 | }, |
| 1249 | { |
| 1250 | name: "array expression over limit", |
| 1251 | expr: "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", |
| 1252 | maxNodes: 5, |
| 1253 | shouldError: true, |
| 1254 | }, |
| 1255 | { |
| 1256 | name: "disabled node budget", |
| 1257 | expr: "1 + (2 + (3 + (4 + (5 + (6 + (7 + 8))))))", |
| 1258 | maxNodes: 0, |
| 1259 | shouldError: false, |
| 1260 | }, |
| 1261 | } |
| 1262 | |
| 1263 | for _, tt := range tests { |
| 1264 | t.Run(tt.name, func(t *testing.T) { |
| 1265 | config := conf.CreateNew() |
| 1266 | config.MaxNodes = tt.maxNodes |
| 1267 | config.Disabled = make(map[string]bool, 0) |
| 1268 | |
| 1269 | _, err := parser.ParseWithConfig(tt.expr, config) |
| 1270 | hasError := err != nil && strings.Contains(err.Error(), "exceeds maximum allowed nodes") |
| 1271 | |
| 1272 | if hasError != tt.shouldError { |
| 1273 | t.Errorf("ParseWithConfig(%q) error = %v, shouldError %v", tt.expr, err, tt.shouldError) |
| 1274 | } |
| 1275 | |
| 1276 | // Verify error message format when expected |
| 1277 | if tt.shouldError && err != nil { |
| 1278 | expected := "compilation failed: expression exceeds maximum allowed nodes" |
| 1279 | if !strings.Contains(err.Error(), expected) { |
| 1280 | t.Errorf("Expected error message to contain %q, got %q", expected, err.Error()) |
| 1281 | } |