recursive-llm-ts 5.0.1 → 5.0.2

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.
@@ -1,291 +0,0 @@
1
- package rlm
2
-
3
- import (
4
- "strings"
5
- "testing"
6
- )
7
-
8
- func TestREPLExecutor_BasicExecution(t *testing.T) {
9
- repl := NewREPLExecutor()
10
-
11
- tests := []struct {
12
- name string
13
- code string
14
- env map[string]interface{}
15
- want string
16
- wantErr bool
17
- }{
18
- {
19
- name: "Simple print",
20
- code: `console.log("Hello World")`,
21
- env: map[string]interface{}{},
22
- want: "Hello World",
23
- },
24
- {
25
- name: "Variable access",
26
- code: `console.log(context)`,
27
- env: map[string]interface{}{"context": "Test Context"},
28
- want: "Test Context",
29
- },
30
- {
31
- name: "String slicing",
32
- code: `console.log(context.slice(0, 5))`,
33
- env: map[string]interface{}{"context": "Hello World"},
34
- want: "Hello",
35
- },
36
- {
37
- name: "len function",
38
- code: `console.log(len(context))`,
39
- env: map[string]interface{}{"context": "Hello"},
40
- want: "5",
41
- },
42
- {
43
- name: "regex findall",
44
- code: `console.log(re.findall("ERROR", context))`,
45
- env: map[string]interface{}{"context": "ERROR 1 ERROR 2", "re": NewRegexHelper()},
46
- want: "ERROR,ERROR",
47
- },
48
- {
49
- name: "Last expression evaluation",
50
- code: `const x = 42; x`,
51
- env: map[string]interface{}{},
52
- want: "42",
53
- },
54
- {
55
- name: "Array operations",
56
- code: `const arr = [1, 2, 3]; console.log(arr.length)`,
57
- env: map[string]interface{}{},
58
- want: "3",
59
- },
60
- }
61
-
62
- for _, tt := range tests {
63
- t.Run(tt.name, func(t *testing.T) {
64
- got, err := repl.Execute(tt.code, tt.env)
65
- if (err != nil) != tt.wantErr {
66
- t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr)
67
- return
68
- }
69
- if !tt.wantErr && got != tt.want {
70
- t.Errorf("Execute() = %q, want %q", got, tt.want)
71
- }
72
- })
73
- }
74
- }
75
-
76
- func TestREPLExecutor_CodeExtraction(t *testing.T) {
77
- repl := NewREPLExecutor()
78
-
79
- tests := []struct {
80
- name string
81
- code string
82
- env map[string]interface{}
83
- want string
84
- }{
85
- {
86
- name: "JavaScript code block",
87
- code: "```javascript\nconsole.log('test')\n```",
88
- env: map[string]interface{}{},
89
- want: "test",
90
- },
91
- {
92
- name: "Python code block (should still extract)",
93
- code: "```python\nconsole.log('test')\n```",
94
- env: map[string]interface{}{},
95
- want: "test",
96
- },
97
- {
98
- name: "Generic code block",
99
- code: "```\nconsole.log('test')\n```",
100
- env: map[string]interface{}{},
101
- want: "test",
102
- },
103
- {
104
- name: "No code block",
105
- code: "console.log('test')",
106
- env: map[string]interface{}{},
107
- want: "test",
108
- },
109
- }
110
-
111
- for _, tt := range tests {
112
- t.Run(tt.name, func(t *testing.T) {
113
- got, err := repl.Execute(tt.code, tt.env)
114
- if err != nil {
115
- t.Errorf("Execute() error = %v", err)
116
- return
117
- }
118
- if got != tt.want {
119
- t.Errorf("Execute() = %q, want %q", got, tt.want)
120
- }
121
- })
122
- }
123
- }
124
-
125
- func TestREPLExecutor_JSBootstrap(t *testing.T) {
126
- repl := NewREPLExecutor()
127
-
128
- tests := []struct {
129
- name string
130
- code string
131
- want string
132
- }{
133
- {
134
- name: "json.loads",
135
- code: `const obj = json.loads('{"key":"value"}'); console.log(obj.key)`,
136
- want: "value",
137
- },
138
- {
139
- name: "json.dumps",
140
- code: `console.log(json.dumps({key: "value"}))`,
141
- want: `{"key":"value"}`,
142
- },
143
- {
144
- name: "range",
145
- code: `console.log(range(5).length)`,
146
- want: "5",
147
- },
148
- {
149
- name: "sum",
150
- code: `console.log(sum([1, 2, 3]))`,
151
- want: "6",
152
- },
153
- {
154
- name: "Counter",
155
- code: `const c = Counter("hello"); console.log(c.l)`,
156
- want: "2",
157
- },
158
- {
159
- name: "sorted",
160
- code: `console.log(sorted([3, 1, 2]).join(','))`,
161
- want: "1,2,3",
162
- },
163
- {
164
- name: "enumerate",
165
- code: `console.log(enumerate(['a', 'b']).length)`,
166
- want: "2",
167
- },
168
- {
169
- name: "zip",
170
- code: `console.log(zip([1, 2], ['a', 'b']).length)`,
171
- want: "2",
172
- },
173
- {
174
- name: "any",
175
- code: `console.log(any([false, true, false]))`,
176
- want: "true",
177
- },
178
- {
179
- name: "all",
180
- code: `console.log(all([true, true, true]))`,
181
- want: "true",
182
- },
183
- }
184
-
185
- for _, tt := range tests {
186
- t.Run(tt.name, func(t *testing.T) {
187
- got, err := repl.Execute(tt.code, map[string]interface{}{})
188
- if err != nil {
189
- t.Errorf("Execute() error = %v", err)
190
- return
191
- }
192
- if got != tt.want {
193
- t.Errorf("Execute() = %q, want %q", got, tt.want)
194
- }
195
- })
196
- }
197
- }
198
-
199
- func TestREPLExecutor_OutputTruncation(t *testing.T) {
200
- repl := NewREPLExecutor()
201
- repl.maxOutputChars = 50
202
-
203
- longOutput := strings.Repeat("x", 100)
204
- code := `console.log("` + longOutput + `")`
205
-
206
- got, err := repl.Execute(code, map[string]interface{}{})
207
- if err != nil {
208
- t.Errorf("Execute() error = %v", err)
209
- return
210
- }
211
-
212
- if len(got) <= repl.maxOutputChars {
213
- t.Errorf("Expected truncation, but got length %d", len(got))
214
- }
215
-
216
- if !strings.Contains(got, "[Output truncated") {
217
- t.Errorf("Expected truncation message in output")
218
- }
219
- }
220
-
221
- func TestREPLExecutor_ErrorHandling(t *testing.T) {
222
- repl := NewREPLExecutor()
223
-
224
- tests := []struct {
225
- name string
226
- code string
227
- wantErr bool
228
- }{
229
- {
230
- name: "Syntax error",
231
- code: `const x = ;`,
232
- wantErr: true,
233
- },
234
- {
235
- name: "Reference error",
236
- code: `console.log(undefinedVariable)`,
237
- wantErr: true,
238
- },
239
- {
240
- name: "Valid code",
241
- code: `console.log("ok")`,
242
- wantErr: false,
243
- },
244
- }
245
-
246
- for _, tt := range tests {
247
- t.Run(tt.name, func(t *testing.T) {
248
- _, err := repl.Execute(tt.code, map[string]interface{}{})
249
- if (err != nil) != tt.wantErr {
250
- t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr)
251
- }
252
- })
253
- }
254
- }
255
-
256
- func TestRegexHelper(t *testing.T) {
257
- re := NewRegexHelper()
258
-
259
- t.Run("findall", func(t *testing.T) {
260
- result := re["findall"]("ERROR", "ERROR 1 ERROR 2 WARNING")
261
- matches, ok := result.([]string)
262
- if !ok {
263
- t.Fatal("Expected []string from findall")
264
- }
265
- if len(matches) != 2 {
266
- t.Errorf("Expected 2 matches, got %d", len(matches))
267
- }
268
- })
269
-
270
- t.Run("search", func(t *testing.T) {
271
- result := re["search"]("ERROR", "INFO ERROR WARNING")
272
- match, ok := result.(string)
273
- if !ok {
274
- t.Fatal("Expected string from search")
275
- }
276
- if match != "ERROR" {
277
- t.Errorf("Expected 'ERROR', got %q", match)
278
- }
279
- })
280
-
281
- t.Run("no match", func(t *testing.T) {
282
- result := re["search"]("ERROR", "INFO WARNING")
283
- match, ok := result.(string)
284
- if !ok {
285
- t.Fatal("Expected string from search")
286
- }
287
- if match != "" {
288
- t.Errorf("Expected empty string, got %q", match)
289
- }
290
- })
291
- }
@@ -1,343 +0,0 @@
1
- package rlm
2
-
3
- import (
4
- "encoding/json"
5
- "testing"
6
- )
7
-
8
- func TestNewSchemaValidator(t *testing.T) {
9
- tests := []struct {
10
- name string
11
- schema *JSONSchema
12
- wantErr bool
13
- }{
14
- {
15
- name: "nil schema",
16
- schema: nil,
17
- wantErr: true,
18
- },
19
- {
20
- name: "simple string schema",
21
- schema: &JSONSchema{
22
- Type: "string",
23
- },
24
- wantErr: false,
25
- },
26
- {
27
- name: "object schema with properties",
28
- schema: &JSONSchema{
29
- Type: "object",
30
- Properties: map[string]*JSONSchema{
31
- "name": {Type: "string"},
32
- "age": {Type: "number"},
33
- },
34
- Required: []string{"name"},
35
- },
36
- wantErr: false,
37
- },
38
- {
39
- name: "array schema",
40
- schema: &JSONSchema{
41
- Type: "array",
42
- Items: &JSONSchema{
43
- Type: "string",
44
- },
45
- },
46
- wantErr: false,
47
- },
48
- {
49
- name: "nested object schema",
50
- schema: &JSONSchema{
51
- Type: "object",
52
- Properties: map[string]*JSONSchema{
53
- "address": {
54
- Type: "object",
55
- Properties: map[string]*JSONSchema{
56
- "street": {Type: "string"},
57
- "city": {Type: "string"},
58
- },
59
- Required: []string{"street", "city"},
60
- },
61
- },
62
- Required: []string{"address"},
63
- },
64
- wantErr: false,
65
- },
66
- }
67
-
68
- for _, tt := range tests {
69
- t.Run(tt.name, func(t *testing.T) {
70
- validator, err := NewSchemaValidator(tt.schema)
71
- if tt.wantErr {
72
- if err == nil {
73
- t.Error("expected error but got nil")
74
- }
75
- return
76
- }
77
- if err != nil {
78
- t.Errorf("unexpected error: %v", err)
79
- return
80
- }
81
- if validator == nil {
82
- t.Error("expected non-nil validator")
83
- }
84
- })
85
- }
86
- }
87
-
88
- func TestSchemaValidatorValidate(t *testing.T) {
89
- schema := &JSONSchema{
90
- Type: "object",
91
- Properties: map[string]*JSONSchema{
92
- "name": {Type: "string"},
93
- "age": {Type: "number"},
94
- },
95
- Required: []string{"name"},
96
- }
97
-
98
- validator, err := NewSchemaValidator(schema)
99
- if err != nil {
100
- t.Fatalf("failed to create validator: %v", err)
101
- }
102
-
103
- tests := []struct {
104
- name string
105
- data interface{}
106
- wantErr bool
107
- }{
108
- {
109
- name: "valid data with all fields",
110
- data: map[string]interface{}{
111
- "name": "Alice",
112
- "age": float64(30),
113
- },
114
- wantErr: false,
115
- },
116
- {
117
- name: "valid data with only required fields",
118
- data: map[string]interface{}{
119
- "name": "Bob",
120
- },
121
- wantErr: false,
122
- },
123
- {
124
- name: "invalid - missing required field",
125
- data: map[string]interface{}{
126
- "age": float64(25),
127
- },
128
- wantErr: true,
129
- },
130
- }
131
-
132
- for _, tt := range tests {
133
- t.Run(tt.name, func(t *testing.T) {
134
- err := validator.Validate(tt.data)
135
- if tt.wantErr && err == nil {
136
- t.Error("expected validation error but got nil")
137
- }
138
- if !tt.wantErr && err != nil {
139
- t.Errorf("unexpected validation error: %v", err)
140
- }
141
- })
142
- }
143
- }
144
-
145
- func TestValidateJSON(t *testing.T) {
146
- schema := &JSONSchema{
147
- Type: "object",
148
- Properties: map[string]*JSONSchema{
149
- "status": {Type: "string"},
150
- },
151
- Required: []string{"status"},
152
- }
153
-
154
- validator, err := NewSchemaValidator(schema)
155
- if err != nil {
156
- t.Fatalf("failed to create validator: %v", err)
157
- }
158
-
159
- validJSON := `{"status": "ok"}`
160
- if err := validator.ValidateJSON([]byte(validJSON)); err != nil {
161
- t.Errorf("expected valid JSON to pass: %v", err)
162
- }
163
-
164
- invalidJSON := `{"missing": "field"}`
165
- if err := validator.ValidateJSON([]byte(invalidJSON)); err == nil {
166
- t.Error("expected invalid JSON to fail validation")
167
- }
168
-
169
- malformedJSON := `{not valid json`
170
- if err := validator.ValidateJSON([]byte(malformedJSON)); err == nil {
171
- t.Error("expected malformed JSON to fail")
172
- }
173
- }
174
-
175
- func TestJSONSchemaConversion(t *testing.T) {
176
- original := &JSONSchema{
177
- Type: "object",
178
- Properties: map[string]*JSONSchema{
179
- "name": {Type: "string"},
180
- "score": {Type: "number"},
181
- },
182
- Required: []string{"name", "score"},
183
- }
184
-
185
- // Convert to Google schema and back
186
- googleSchema, err := JSONSchemaToGoogleSchema(original)
187
- if err != nil {
188
- t.Fatalf("failed to convert to Google schema: %v", err)
189
- }
190
- if googleSchema == nil {
191
- t.Fatal("expected non-nil Google schema")
192
- }
193
-
194
- roundTrip, err := GoogleSchemaToJSONSchema(googleSchema)
195
- if err != nil {
196
- t.Fatalf("failed to convert back: %v", err)
197
- }
198
- if roundTrip.Type != original.Type {
199
- t.Errorf("type mismatch: got %s, want %s", roundTrip.Type, original.Type)
200
- }
201
- }
202
-
203
- func TestInferSchemaFromJSON(t *testing.T) {
204
- tests := []struct {
205
- name string
206
- json string
207
- expectedType string
208
- }{
209
- {
210
- name: "simple object",
211
- json: `{"name": "Alice", "age": 30}`,
212
- expectedType: "object",
213
- },
214
- {
215
- name: "array",
216
- json: `[1, 2, 3]`,
217
- expectedType: "array",
218
- },
219
- {
220
- name: "string",
221
- json: `"hello"`,
222
- expectedType: "string",
223
- },
224
- {
225
- name: "number",
226
- json: `3.14`,
227
- expectedType: "number",
228
- },
229
- {
230
- name: "integer",
231
- json: `42`,
232
- expectedType: "integer",
233
- },
234
- {
235
- name: "boolean",
236
- json: `true`,
237
- expectedType: "boolean",
238
- },
239
- {
240
- name: "null",
241
- json: `null`,
242
- expectedType: "null",
243
- },
244
- }
245
-
246
- for _, tt := range tests {
247
- t.Run(tt.name, func(t *testing.T) {
248
- schema, err := InferSchemaFromJSON([]byte(tt.json))
249
- if err != nil {
250
- t.Fatalf("unexpected error: %v", err)
251
- }
252
- if schema.Type != tt.expectedType {
253
- t.Errorf("got type %s, want %s", schema.Type, tt.expectedType)
254
- }
255
- })
256
- }
257
- }
258
-
259
- func TestInferSchemaFromJSON_NestedObject(t *testing.T) {
260
- jsonData := `{
261
- "user": {
262
- "name": "Alice",
263
- "scores": [95, 87, 92]
264
- }
265
- }`
266
-
267
- schema, err := InferSchemaFromJSON([]byte(jsonData))
268
- if err != nil {
269
- t.Fatalf("unexpected error: %v", err)
270
- }
271
-
272
- if schema.Type != "object" {
273
- t.Fatalf("expected object, got %s", schema.Type)
274
- }
275
-
276
- userProp, ok := schema.Properties["user"]
277
- if !ok {
278
- t.Fatal("missing 'user' property")
279
- }
280
- if userProp.Type != "object" {
281
- t.Errorf("expected user to be object, got %s", userProp.Type)
282
- }
283
-
284
- scoresProp, ok := userProp.Properties["scores"]
285
- if !ok {
286
- t.Fatal("missing 'scores' property")
287
- }
288
- if scoresProp.Type != "array" {
289
- t.Errorf("expected scores to be array, got %s", scoresProp.Type)
290
- }
291
- }
292
-
293
- func TestSchemaJSONRoundTrip(t *testing.T) {
294
- // Test that our JSONSchema type serializes/deserializes correctly
295
- minLen := 5
296
- maxLen := 100
297
- min := 1.0
298
- max := 10.0
299
-
300
- original := &JSONSchema{
301
- Type: "object",
302
- Properties: map[string]*JSONSchema{
303
- "name": {
304
- Type: "string",
305
- MinLength: &minLen,
306
- MaxLength: &maxLen,
307
- },
308
- "score": {
309
- Type: "number",
310
- Minimum: &min,
311
- Maximum: &max,
312
- },
313
- "tags": {
314
- Type: "array",
315
- Items: &JSONSchema{
316
- Type: "string",
317
- Enum: []string{"a", "b", "c"},
318
- },
319
- },
320
- },
321
- Required: []string{"name", "score"},
322
- }
323
-
324
- data, err := json.Marshal(original)
325
- if err != nil {
326
- t.Fatalf("failed to marshal: %v", err)
327
- }
328
-
329
- var parsed JSONSchema
330
- if err := json.Unmarshal(data, &parsed); err != nil {
331
- t.Fatalf("failed to unmarshal: %v", err)
332
- }
333
-
334
- if parsed.Type != "object" {
335
- t.Errorf("type mismatch: got %s", parsed.Type)
336
- }
337
- if len(parsed.Properties) != 3 {
338
- t.Errorf("expected 3 properties, got %d", len(parsed.Properties))
339
- }
340
- if len(parsed.Required) != 2 {
341
- t.Errorf("expected 2 required, got %d", len(parsed.Required))
342
- }
343
- }