datagrok-tools 6.5.4 → 6.5.6

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,197 +0,0 @@
1
- import {describe, it, expect, beforeEach, afterEach} from 'vitest';
2
- import * as os from 'os';
3
- import * as fs from 'fs';
4
- import * as path from 'path';
5
- import {parseFuncCall, buildInlineManifest, resolveManifestSources} from '../commands/server';
6
-
7
- describe('parseFuncCall', () => {
8
- it('parses a single string argument', () => {
9
- expect(parseFuncCall('Chem:smilesToMw("ccc")')).toEqual({
10
- name: 'Chem:smilesToMw',
11
- params: {'0': 'ccc'},
12
- });
13
- });
14
-
15
- it('parses a single-quoted string argument', () => {
16
- expect(parseFuncCall("Pkg:fn('hello')")).toEqual({
17
- name: 'Pkg:fn',
18
- params: {'0': 'hello'},
19
- });
20
- });
21
-
22
- it('parses a numeric argument', () => {
23
- expect(parseFuncCall('Pkg:fn(42)')).toEqual({
24
- name: 'Pkg:fn',
25
- params: {'0': 42},
26
- });
27
- });
28
-
29
- it('parses multiple positional arguments', () => {
30
- expect(parseFuncCall('Pkg:fn(1, "hello", 3.14)')).toEqual({
31
- name: 'Pkg:fn',
32
- params: {'0': 1, '1': 'hello', '2': 3.14},
33
- });
34
- });
35
-
36
- it('parses an object argument with quoted keys', () => {
37
- expect(parseFuncCall('Pkg:fn({"a":5,"b":22})')).toEqual({
38
- name: 'Pkg:fn',
39
- params: {a: 5, b: 22},
40
- });
41
- });
42
-
43
- it('parses an object argument with unquoted keys', () => {
44
- expect(parseFuncCall('Pkg:fn({a:5,b:22})')).toEqual({
45
- name: 'Pkg:fn',
46
- params: {a: 5, b: 22},
47
- });
48
- });
49
-
50
- it('parses an object argument with a boolean value', () => {
51
- expect(parseFuncCall('Pkg:fn({unquoted:true})')).toEqual({
52
- name: 'Pkg:fn',
53
- params: {unquoted: true},
54
- });
55
- });
56
-
57
- it('returns empty params for a no-argument call', () => {
58
- expect(parseFuncCall('Pkg:fn()')).toEqual({
59
- name: 'Pkg:fn',
60
- params: {},
61
- });
62
- });
63
-
64
- it('returns empty params when there are no parentheses', () => {
65
- expect(parseFuncCall('Pkg:fn')).toEqual({
66
- name: 'Pkg:fn',
67
- params: {},
68
- });
69
- });
70
-
71
- it('handles a package-less function name', () => {
72
- expect(parseFuncCall('myFunc("arg")')).toEqual({
73
- name: 'myFunc',
74
- params: {'0': 'arg'},
75
- });
76
- });
77
- });
78
-
79
- describe('buildInlineManifest', () => {
80
- it('sets action to entity.verb', () => {
81
- const result = buildInlineManifest('users', 'delete', ['abc']);
82
- expect(result.operations[0].action).toBe('users.delete');
83
- });
84
-
85
- it('maps string args to {id} param for non-file entities', () => {
86
- const result = buildInlineManifest('users', 'delete', ['id1', 'id2']);
87
- expect(result.operations).toEqual([
88
- {id: 'op0', action: 'users.delete', params: {id: 'id1'}},
89
- {id: 'op1', action: 'users.delete', params: {id: 'id2'}},
90
- ]);
91
- });
92
-
93
- it('maps string args to {path} param for the files entity', () => {
94
- const result = buildInlineManifest('files', 'delete', ['System:AppData/a.txt', 'System:AppData/b.txt']);
95
- expect(result.operations).toEqual([
96
- {id: 'op0', action: 'files.delete', params: {path: 'System:AppData/a.txt'}},
97
- {id: 'op1', action: 'files.delete', params: {path: 'System:AppData/b.txt'}},
98
- ]);
99
- });
100
-
101
- it('passes object array args through as params directly', () => {
102
- const objs = [{name: 'Alice', email: 'a@x.com'}, {name: 'Bob', email: 'b@x.com'}];
103
- const result = buildInlineManifest('users', 'create', objs);
104
- expect(result.operations).toEqual([
105
- {id: 'op0', action: 'users.create', params: {name: 'Alice', email: 'a@x.com'}},
106
- {id: 'op1', action: 'users.create', params: {name: 'Bob', email: 'b@x.com'}},
107
- ]);
108
- });
109
-
110
- it('assigns sequential op ids starting at op0', () => {
111
- const result = buildInlineManifest('groups', 'delete', ['x', 'y', 'z']);
112
- expect(result.operations.map((o) => o.id)).toEqual(['op0', 'op1', 'op2']);
113
- });
114
-
115
- it('returns a single operation for a single arg', () => {
116
- const result = buildInlineManifest('connections', 'delete', ['conn-id']);
117
- expect(result.operations).toHaveLength(1);
118
- });
119
- });
120
-
121
- describe('resolveManifestSources', () => {
122
- let tmpFile: string;
123
-
124
- beforeEach(() => {
125
- tmpFile = path.join(os.tmpdir(), `grok-batch-test-${Date.now()}.bin`);
126
- });
127
-
128
- afterEach(() => {
129
- if (fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile);
130
- });
131
-
132
- it('passes through a manifest with no files.put operations unchanged', () => {
133
- const manifest = {
134
- operations: [{id: 'op0', action: 'users.delete', params: {id: 'abc'}}],
135
- };
136
- expect(resolveManifestSources(manifest)).toEqual(manifest);
137
- });
138
-
139
- it('passes through a files.put operation that has no source field', () => {
140
- const manifest = {
141
- operations: [{id: 'op0', action: 'files.put', params: {path: 'System:AppData/f.txt', content: 'aGk='}}],
142
- };
143
- expect(resolveManifestSources(manifest)).toEqual(manifest);
144
- });
145
-
146
- it('reads the source file, base64-encodes its contents, and removes the source key', () => {
147
- const data = 'hello world';
148
- fs.writeFileSync(tmpFile, data);
149
-
150
- const manifest = {
151
- operations: [{id: 'op0', action: 'files.put', params: {path: 'System:AppData/f.txt', source: tmpFile}}],
152
- };
153
- const result = resolveManifestSources(manifest);
154
- const params = result.operations[0].params as any;
155
- expect(params.content).toBe(Buffer.from(data).toString('base64'));
156
- expect(params.source).toBeUndefined();
157
- });
158
-
159
- it('preserves other params alongside the injected content', () => {
160
- fs.writeFileSync(tmpFile, 'data');
161
-
162
- const manifest = {
163
- operations: [{id: 'op0', action: 'files.put', params: {path: 'System:AppData/f.txt', source: tmpFile, extra: 'val'}}],
164
- };
165
- const result = resolveManifestSources(manifest);
166
- const params = result.operations[0].params as any;
167
- expect(params.path).toBe('System:AppData/f.txt');
168
- expect(params.extra).toBe('val');
169
- });
170
-
171
- it('leaves non-files.put operations untouched in a mixed manifest', () => {
172
- fs.writeFileSync(tmpFile, 'x');
173
-
174
- const manifest = {
175
- operations: [
176
- {id: 'op0', action: 'users.delete', params: {id: 'u1'}},
177
- {id: 'op1', action: 'files.put', params: {path: 'System:AppData/f.txt', source: tmpFile}},
178
- ],
179
- };
180
- const result = resolveManifestSources(manifest);
181
- expect(result.operations[0]).toEqual({id: 'op0', action: 'users.delete', params: {id: 'u1'}});
182
- expect((result.operations[1].params as any).source).toBeUndefined();
183
- expect((result.operations[1].params as any).content).toBeDefined();
184
- });
185
-
186
- it('correctly encodes binary file contents', () => {
187
- const bytes = Buffer.from([0x00, 0xff, 0x10, 0xab]);
188
- fs.writeFileSync(tmpFile, bytes);
189
-
190
- const manifest = {
191
- operations: [{id: 'op0', action: 'files.put', params: {path: 'System:AppData/f.bin', source: tmpFile}}],
192
- };
193
- const result = resolveManifestSources(manifest);
194
- const decoded = Buffer.from((result.operations[0].params as any).content, 'base64');
195
- expect(decoded).toEqual(bytes);
196
- });
197
- });