@openvcs/sdk 0.2.4 → 0.2.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.
@@ -0,0 +1,169 @@
1
+ // Copyright © 2025-2026 OpenVCS Contributors
2
+ // SPDX-License-Identifier: GPL-3.0-or-later
3
+
4
+ const assert = require('node:assert/strict');
5
+ const test = require('node:test');
6
+
7
+ const { VcsDelegateBase } = require('../lib/runtime');
8
+
9
+ /**
10
+ * Compile-time override safety lives in `vcs-delegate-base.types.ts`, which
11
+ * verifies that generic parameters are explicit and incompatible signatures
12
+ * (wrong return type or params) fail the TypeScript compiler. This file
13
+ * tests runtime behavior using the plain Node test runner.
14
+ * @typedef {{ message: string, method: string }} CommitCall */
15
+
16
+ class ExampleVcsDelegates extends VcsDelegateBase {
17
+ /** @param {{ prefix: string, calls: CommitCall[] }} deps */
18
+ constructor(deps) {
19
+ super(deps);
20
+ }
21
+
22
+ getCaps() {
23
+ return {
24
+ commits: true,
25
+ branches: true,
26
+ tags: false,
27
+ staging: true,
28
+ push_pull: false,
29
+ fast_forward: true,
30
+ };
31
+ }
32
+
33
+ async commit(params, context) {
34
+ this.deps.calls.push({ message: params.message, method: context.method });
35
+ return `${this.deps.prefix}:${params.message}`;
36
+ }
37
+ }
38
+
39
+ class SharedBranchDelegates extends VcsDelegateBase {
40
+ /** @param {{}} deps */
41
+ constructor(deps) {
42
+ super(deps);
43
+ }
44
+
45
+ getCurrentBranch(params) {
46
+ return `branch:${params.session_id}`;
47
+ }
48
+ }
49
+
50
+ class DerivedBranchDelegates extends SharedBranchDelegates {
51
+ listBranches() {
52
+ return [];
53
+ }
54
+ }
55
+
56
+ test('VcsDelegateBase maps overridden camelCase methods to rpc delegates', async () => {
57
+ const delegate = new ExampleVcsDelegates({ prefix: 'commit', calls: [] });
58
+ const delegates = delegate.toDelegates();
59
+
60
+ assert.deepEqual(Object.keys(delegates).sort(), ['vcs.commit', 'vcs.get_caps']);
61
+ assert.equal(typeof delegates['vcs.get_caps'], 'function');
62
+ assert.equal(typeof delegates['vcs.commit'], 'function');
63
+
64
+ assert.deepEqual(await delegates['vcs.get_caps']({}, {}), {
65
+ commits: true,
66
+ branches: true,
67
+ tags: false,
68
+ staging: true,
69
+ push_pull: false,
70
+ fast_forward: true,
71
+ });
72
+
73
+ assert.equal(
74
+ await delegates['vcs.commit'](
75
+ {
76
+ session_id: 'session-1',
77
+ name: 'OpenVCS',
78
+ email: 'team@example.com',
79
+ message: 'ship it',
80
+ },
81
+ { host: {}, method: 'vcs.commit', requestId: 7 },
82
+ ),
83
+ 'commit:ship it',
84
+ );
85
+ assert.deepEqual(delegate.deps.calls, [
86
+ { message: 'ship it', method: 'vcs.commit' },
87
+ ]);
88
+ });
89
+
90
+ test('VcsDelegateBase keeps inherited overrides when building delegates', async () => {
91
+ const delegates = new DerivedBranchDelegates({}).toDelegates();
92
+
93
+ assert.deepEqual(Object.keys(delegates).sort(), [
94
+ 'vcs.get_current_branch',
95
+ 'vcs.list_branches',
96
+ ]);
97
+ assert.equal(
98
+ await delegates['vcs.get_current_branch'](
99
+ { session_id: 'session-2' },
100
+ { host: {}, method: 'vcs.get_current_branch', requestId: 8 },
101
+ ),
102
+ 'branch:session-2',
103
+ );
104
+ assert.deepEqual(
105
+ await delegates['vcs.list_branches'](
106
+ { session_id: 'session-2' },
107
+ { host: {}, method: 'vcs.list_branches', requestId: 9 },
108
+ ),
109
+ [],
110
+ );
111
+ });
112
+
113
+ test('VcsDelegateBase returns a fresh delegate map on each call', async () => {
114
+ const delegate = new ExampleVcsDelegates({ prefix: 'repeat', calls: [] });
115
+ const firstDelegates = delegate.toDelegates();
116
+ const secondDelegates = delegate.toDelegates();
117
+
118
+ assert.notStrictEqual(firstDelegates, secondDelegates);
119
+ assert.notStrictEqual(
120
+ firstDelegates['vcs.commit'],
121
+ secondDelegates['vcs.commit'],
122
+ );
123
+ assert.equal(
124
+ await secondDelegates['vcs.commit'](
125
+ {
126
+ session_id: 'session-3',
127
+ name: 'OpenVCS',
128
+ email: 'team@example.com',
129
+ message: 'again',
130
+ },
131
+ { host: {}, method: 'vcs.commit', requestId: 10 },
132
+ ),
133
+ 'repeat:again',
134
+ );
135
+ });
136
+
137
+ test('VcsDelegateBase throws with the exact error message for base stubs', () => {
138
+ const delegate = new ExampleVcsDelegates({ prefix: 'x', calls: [] });
139
+
140
+ // Each base stub throws with a message that includes the method name.
141
+ // Test a representative subset; all stubs share the same formatter.
142
+ const expectedMessage = "VCS delegate method 'cloneRepo' must be overridden before registration";
143
+ let thrown;
144
+ try {
145
+ delegate.cloneRepo({ url: 'x', dest: 'y' }, {});
146
+ } catch (e) {
147
+ thrown = e;
148
+ }
149
+ assert.ok(thrown instanceof Error, 'should throw an Error');
150
+ assert.strictEqual(thrown.message, expectedMessage);
151
+
152
+ // Verify a second stub throws with its own method name in the message.
153
+ const expectedMessage2 = "VCS delegate method 'stashPush' must be overridden before registration";
154
+ let thrown2;
155
+ try {
156
+ delegate.stashPush({ session_id: 's' }, {});
157
+ } catch (e) {
158
+ thrown2 = e;
159
+ }
160
+ assert.ok(thrown2 instanceof Error);
161
+ assert.strictEqual(thrown2.message, expectedMessage2);
162
+ });
163
+
164
+ test('VcsDelegateBase accepts an empty deps object without errors', () => {
165
+ assert.doesNotThrow(() => new ExampleVcsDelegates({ prefix: '', calls: [] }));
166
+ assert.doesNotThrow(() => new SharedBranchDelegates({}));
167
+ const delegates = new SharedBranchDelegates({}).toDelegates();
168
+ assert.deepEqual(Object.keys(delegates), ['vcs.get_current_branch']);
169
+ });
@@ -0,0 +1,44 @@
1
+ // Copyright © 2025-2026 OpenVCS Contributors
2
+ // SPDX-License-Identifier: GPL-3.0-or-later
3
+
4
+ import type { RequestParams, VcsCapabilities } from '../src/lib/types';
5
+
6
+ import {
7
+ VcsDelegateBase,
8
+ type PluginRuntimeContext,
9
+ } from '../src/lib/runtime';
10
+
11
+ type CommitCall = {
12
+ message: string;
13
+ method: string;
14
+ };
15
+
16
+ class TypedExampleVcsDelegates extends VcsDelegateBase<{
17
+ prefix: string;
18
+ calls: CommitCall[];
19
+ }> {
20
+ override getCaps(
21
+ _params: RequestParams,
22
+ _context: PluginRuntimeContext,
23
+ ): VcsCapabilities {
24
+ return {
25
+ commits: true,
26
+ branches: true,
27
+ tags: false,
28
+ staging: true,
29
+ push_pull: false,
30
+ fast_forward: true,
31
+ };
32
+ }
33
+ }
34
+
35
+ new TypedExampleVcsDelegates({ prefix: 'commit', calls: [] }).toDelegates();
36
+
37
+ class InvalidVcsDelegates extends VcsDelegateBase<{}> {
38
+ // @ts-expect-error `getCaps` must return `VcsCapabilities`.
39
+ override getCaps(): string {
40
+ return 'invalid';
41
+ }
42
+ }
43
+
44
+ new InvalidVcsDelegates({});