@mcp-abap-adt/adt-clients 3.6.0 → 3.7.0

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.
@@ -4,31 +4,41 @@
4
4
  * Extends AdtUnitTest and overrides run/status/result to use legacy endpoints:
5
5
  * - /sap/bc/adt/abapunit/testruns instead of /sap/bc/adt/abapunit/runs
6
6
  * - application/xml content types instead of versioned vnd.sap.adt.api.abapunit.* types
7
+ *
8
+ * Key difference: Legacy systems return results synchronously (aunit:runResult)
9
+ * from the POST to /testruns — no run ID, no async polling needed.
7
10
  */
8
11
  import type { IAdtResponse as AxiosResponse, IAbapConnection, IAdtOperationOptions, ILogger } from '@mcp-abap-adt/interfaces';
9
12
  import { AdtUnitTest } from './AdtUnitTest';
10
- import type { IUnitTestConfig, IUnitTestState } from './types';
13
+ import type { IClassUnitTestDefinition, IClassUnitTestRunOptions, IUnitTestConfig, IUnitTestState } from './types';
11
14
  export declare class AdtUnitTestLegacy extends AdtUnitTest {
12
15
  constructor(connection: IAbapConnection, logger?: ILogger);
13
16
  /**
14
- * Create unit test run using legacy endpoint
17
+ * Create unit test run using legacy endpoint.
18
+ * Legacy returns results synchronously — no run ID or polling needed.
15
19
  */
16
20
  create(config: IUnitTestConfig, _options?: IAdtOperationOptions): Promise<IUnitTestState>;
17
21
  /**
18
- * Get unit test status using legacy endpoint
22
+ * Run unit tests legacy returns results synchronously.
23
+ */
24
+ run(tests: IClassUnitTestDefinition[], options?: IClassUnitTestRunOptions): Promise<string>;
25
+ /**
26
+ * Get unit test status — legacy returns results synchronously,
27
+ * so this returns the cached response from create().
19
28
  */
20
- getStatus(runId: string, withLongPolling?: boolean): Promise<AxiosResponse>;
29
+ getStatus(_runId: string, _withLongPolling?: boolean): Promise<AxiosResponse>;
21
30
  /**
22
- * Get unit test result using legacy endpoint
31
+ * Get unit test result legacy returns results synchronously,
32
+ * so this returns the cached response from create().
23
33
  */
24
- getResult(runId: string, options?: {
34
+ getResult(_runId: string, _options?: {
25
35
  withNavigationUris?: boolean;
26
36
  format?: 'abapunit' | 'junit';
27
37
  }): Promise<AxiosResponse>;
28
38
  /**
29
- * Extract run ID from legacy response
30
- * Legacy uses /testruns/ in URIs instead of /runs/
39
+ * Read unit test legacy returns results synchronously,
40
+ * so this returns the cached result from create().
31
41
  */
32
- protected extractRunId(response: AxiosResponse): string | undefined;
42
+ read(_config: Partial<IUnitTestConfig>, _version?: 'active' | 'inactive'): Promise<IUnitTestState | undefined>;
33
43
  }
34
44
  //# sourceMappingURL=AdtUnitTestLegacy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdtUnitTestLegacy.d.ts","sourceRoot":"","sources":["../../../src/core/unitTest/AdtUnitTestLegacy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,YAAY,IAAI,aAAa,EAC7B,eAAe,EACf,oBAAoB,EACpB,OAAO,EACR,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAM5C,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/D,qBAAa,iBAAkB,SAAQ,WAAW;gBACpC,UAAU,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,OAAO;IAIzD;;OAEG;IACY,MAAM,CACnB,MAAM,EAAE,eAAe,EACvB,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,cAAc,CAAC;IAuC1B;;OAEG;IACY,SAAS,CACtB,KAAK,EAAE,MAAM,EACb,eAAe,GAAE,OAAc,GAC9B,OAAO,CAAC,aAAa,CAAC;IAUzB;;OAEG;IACY,SAAS,CACtB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;QAAE,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAA;KAAE,GACxE,OAAO,CAAC,aAAa,CAAC;IAUzB;;;OAGG;cACgB,YAAY,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS;CAqC7E"}
1
+ {"version":3,"file":"AdtUnitTestLegacy.d.ts","sourceRoot":"","sources":["../../../src/core/unitTest/AdtUnitTestLegacy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,YAAY,IAAI,aAAa,EAC7B,eAAe,EACf,oBAAoB,EACpB,OAAO,EACR,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EACxB,eAAe,EACf,cAAc,EACf,MAAM,SAAS,CAAC;AAKjB,qBAAa,iBAAkB,SAAQ,WAAW;gBACpC,UAAU,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,OAAO;IAIzD;;;OAGG;IACY,MAAM,CACnB,MAAM,EAAE,eAAe,EACvB,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,cAAc,CAAC;IAkC1B;;OAEG;IACY,GAAG,CAChB,KAAK,EAAE,wBAAwB,EAAE,EACjC,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,MAAM,CAAC;IAKlB;;;OAGG;IACY,SAAS,CACtB,MAAM,EAAE,MAAM,EACd,gBAAgB,GAAE,OAAc,GAC/B,OAAO,CAAC,aAAa,CAAC;IASzB;;;OAGG;IACY,SAAS,CACtB,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE;QAAE,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAA;KAAE,GACzE,OAAO,CAAC,aAAa,CAAC;IASzB;;;OAGG;IACY,IAAI,CACjB,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,EACjC,QAAQ,GAAE,QAAQ,GAAG,UAAqB,GACzC,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;CAWvC"}
@@ -5,18 +5,23 @@
5
5
  * Extends AdtUnitTest and overrides run/status/result to use legacy endpoints:
6
6
  * - /sap/bc/adt/abapunit/testruns instead of /sap/bc/adt/abapunit/runs
7
7
  * - application/xml content types instead of versioned vnd.sap.adt.api.abapunit.* types
8
+ *
9
+ * Key difference: Legacy systems return results synchronously (aunit:runResult)
10
+ * from the POST to /testruns — no run ID, no async polling needed.
8
11
  */
9
12
  Object.defineProperty(exports, "__esModule", { value: true });
10
13
  exports.AdtUnitTestLegacy = void 0;
11
- const internalUtils_1 = require("../../utils/internalUtils");
12
14
  const AdtUnitTest_1 = require("./AdtUnitTest");
13
15
  const runLegacy_1 = require("./runLegacy");
16
+ /** Synthetic run ID for legacy synchronous results */
17
+ const LEGACY_SYNC_RUN_ID = 'legacy-sync';
14
18
  class AdtUnitTestLegacy extends AdtUnitTest_1.AdtUnitTest {
15
19
  constructor(connection, logger) {
16
20
  super(connection, logger);
17
21
  }
18
22
  /**
19
- * Create unit test run using legacy endpoint
23
+ * Create unit test run using legacy endpoint.
24
+ * Legacy returns results synchronously — no run ID or polling needed.
20
25
  */
21
26
  async create(config, _options) {
22
27
  if (!config.tests || config.tests.length === 0) {
@@ -26,16 +31,15 @@ class AdtUnitTestLegacy extends AdtUnitTest_1.AdtUnitTest {
26
31
  this.logger?.info?.('Starting unit test run (legacy)');
27
32
  const response = await (0, runLegacy_1.startClassUnitTestRunLegacy)(this.connection, config.tests, config.options);
28
33
  this.logger?.debug?.('Unit test run response status:', response.status);
29
- const runId = this.extractRunId(response);
30
- if (!runId) {
31
- this.logger?.error?.('Failed to extract run ID from response. Response data:', response.data);
32
- throw new Error('Failed to start unit test run: run ID not returned');
33
- }
34
- this.logger?.info?.('Unit test run started (legacy), run ID:', runId);
35
- this.lastRunId = runId;
34
+ // Legacy returns results synchronously — store as both status and result
35
+ this.lastStatusResponse = response;
36
+ this.lastResultResponse = response;
37
+ this.lastRunId = LEGACY_SYNC_RUN_ID;
38
+ this.logger?.info?.('Unit test run completed (legacy, synchronous)');
36
39
  return {
37
40
  createResult: response,
38
- runId,
41
+ runId: LEGACY_SYNC_RUN_ID,
42
+ runResult: response.data,
39
43
  errors: [],
40
44
  };
41
45
  }
@@ -45,57 +49,46 @@ class AdtUnitTestLegacy extends AdtUnitTest_1.AdtUnitTest {
45
49
  }
46
50
  }
47
51
  /**
48
- * Get unit test status using legacy endpoint
52
+ * Run unit tests legacy returns results synchronously.
49
53
  */
50
- async getStatus(runId, withLongPolling = true) {
51
- const response = await (0, runLegacy_1.getClassUnitTestStatusLegacy)(this.connection, runId, withLongPolling);
52
- this.lastStatusResponse = response;
53
- return response;
54
+ async run(tests, options) {
55
+ await this.create({ tests, options });
56
+ return LEGACY_SYNC_RUN_ID;
54
57
  }
55
58
  /**
56
- * Get unit test result using legacy endpoint
59
+ * Get unit test status legacy returns results synchronously,
60
+ * so this returns the cached response from create().
57
61
  */
58
- async getResult(runId, options) {
59
- const response = await (0, runLegacy_1.getClassUnitTestResultLegacy)(this.connection, runId, options);
60
- this.lastResultResponse = response;
61
- return response;
62
+ async getStatus(_runId, _withLongPolling = true) {
63
+ if (this.lastStatusResponse) {
64
+ return this.lastStatusResponse;
65
+ }
66
+ throw new Error('No status available. Legacy systems return results synchronously via create().');
62
67
  }
63
68
  /**
64
- * Extract run ID from legacy response
65
- * Legacy uses /testruns/ in URIs instead of /runs/
69
+ * Get unit test result legacy returns results synchronously,
70
+ * so this returns the cached response from create().
66
71
  */
67
- extractRunId(response) {
68
- // Try headers first
69
- const locationHeader = (0, internalUtils_1.headerValueToString)(response.headers?.location) ||
70
- (0, internalUtils_1.headerValueToString)(response.headers?.['content-location']) ||
71
- (0, internalUtils_1.headerValueToString)(response.headers?.['sap-adt-location']);
72
- if (locationHeader) {
73
- const match = locationHeader.match(/\/testruns\/([^/]+)/) ||
74
- locationHeader.match(/\/runs\/([^/]+)/);
75
- if (match) {
76
- return match[1];
77
- }
78
- }
79
- // Fallback: parse from response body (XML)
80
- const data = response.data;
81
- if (typeof data === 'string') {
82
- const uriMatch = data.match(/uri="([^"]+)"/);
83
- if (uriMatch) {
84
- const uri = uriMatch[1];
85
- const match = uri.match(/\/testruns\/([^/]+)/) || uri.match(/\/runs\/([^/]+)/);
86
- if (match) {
87
- return match[1];
88
- }
89
- }
72
+ async getResult(_runId, _options) {
73
+ if (this.lastResultResponse) {
74
+ return this.lastResultResponse;
90
75
  }
91
- else if (data?.uri) {
92
- const match = data.uri.match(/\/testruns\/([^/]+)/) ||
93
- data.uri.match(/\/runs\/([^/]+)/);
94
- if (match) {
95
- return match[1];
96
- }
76
+ throw new Error('No result available. Legacy systems return results synchronously via create().');
77
+ }
78
+ /**
79
+ * Read unit test — legacy returns results synchronously,
80
+ * so this returns the cached result from create().
81
+ */
82
+ async read(_config, _version = 'active') {
83
+ if (!this.lastResultResponse) {
84
+ return undefined;
97
85
  }
98
- return undefined;
86
+ return {
87
+ runId: LEGACY_SYNC_RUN_ID,
88
+ runStatus: this.lastStatusResponse?.data,
89
+ runResult: this.lastResultResponse.data,
90
+ errors: [],
91
+ };
99
92
  }
100
93
  }
101
94
  exports.AdtUnitTestLegacy = AdtUnitTestLegacy;
@@ -9,9 +9,15 @@ import type { IAdtResponse as AxiosResponse, IAbapConnection } from '@mcp-abap-a
9
9
  import type { IClassUnitTestDefinition, IClassUnitTestRunOptions } from './types';
10
10
  /**
11
11
  * Start ABAP Unit test run on legacy systems
12
- * Uses /sap/bc/adt/abapunit/testruns endpoint with application/xml content type
12
+ * Uses /sap/bc/adt/abapunit/testruns endpoint with aunit:runConfiguration format
13
+ *
14
+ * Legacy format differs from modern:
15
+ * - Root element: aunit:runConfiguration (not aunit:run)
16
+ * - Namespace: http://www.sap.com/adt/aunit (not http://www.sap.com/adt/api/aunit)
17
+ * - Objects via adtcore:objectReferences with URI (not aunit:tests with containerClass/class)
18
+ * - Content-Type/Accept: application/xml (not versioned vnd.sap.adt.api.abapunit.*)
13
19
  */
14
- export declare function startClassUnitTestRunLegacy(connection: IAbapConnection, tests: IClassUnitTestDefinition[], options?: IClassUnitTestRunOptions): Promise<AxiosResponse>;
20
+ export declare function startClassUnitTestRunLegacy(connection: IAbapConnection, tests: IClassUnitTestDefinition[], _options?: IClassUnitTestRunOptions): Promise<AxiosResponse>;
15
21
  export declare function getClassUnitTestStatusLegacy(connection: IAbapConnection, runId: string, withLongPolling?: boolean): Promise<AxiosResponse>;
16
22
  export declare function getClassUnitTestResultLegacy(connection: IAbapConnection, runId: string, options?: {
17
23
  withNavigationUris?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"runLegacy.d.ts","sourceRoot":"","sources":["../../../src/core/unitTest/runLegacy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,YAAY,IAAI,aAAa,EAC7B,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,SAAS,CAAC;AASjB;;;GAGG;AACH,wBAAsB,2BAA2B,CAC/C,UAAU,EAAE,eAAe,EAC3B,KAAK,EAAE,wBAAwB,EAAE,EACjC,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,aAAa,CAAC,CAiDxB;AAED,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,eAAe,EAC3B,KAAK,EAAE,MAAM,EACb,eAAe,GAAE,OAAc,GAC9B,OAAO,CAAC,aAAa,CAAC,CAaxB;AAED,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,eAAe,EAC3B,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;IAAE,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAE,GACzC,OAAO,CAAC,aAAa,CAAC,CAkBxB"}
1
+ {"version":3,"file":"runLegacy.d.ts","sourceRoot":"","sources":["../../../src/core/unitTest/runLegacy.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,YAAY,IAAI,aAAa,EAC7B,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,SAAS,CAAC;AAKjB;;;;;;;;;GASG;AACH,wBAAsB,2BAA2B,CAC/C,UAAU,EAAE,eAAe,EAC3B,KAAK,EAAE,wBAAwB,EAAE,EACjC,QAAQ,CAAC,EAAE,wBAAwB,GAClC,OAAO,CAAC,aAAa,CAAC,CAmCxB;AAED,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,eAAe,EAC3B,KAAK,EAAE,MAAM,EACb,eAAe,GAAE,OAAc,GAC9B,OAAO,CAAC,aAAa,CAAC,CAaxB;AAED,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,eAAe,EAC3B,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;IAAE,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAAE,GACzC,OAAO,CAAC,aAAa,CAAC,CAkBxB"}
@@ -14,45 +14,38 @@ const internalUtils_1 = require("../../utils/internalUtils");
14
14
  const timeouts_1 = require("../../utils/timeouts");
15
15
  const CT_XML = 'application/xml';
16
16
  const ACCEPT_XML = 'application/xml';
17
- function boolAttr(value, fallback) {
18
- return (value ?? fallback) ? 'true' : 'false';
19
- }
20
17
  /**
21
18
  * Start ABAP Unit test run on legacy systems
22
- * Uses /sap/bc/adt/abapunit/testruns endpoint with application/xml content type
19
+ * Uses /sap/bc/adt/abapunit/testruns endpoint with aunit:runConfiguration format
20
+ *
21
+ * Legacy format differs from modern:
22
+ * - Root element: aunit:runConfiguration (not aunit:run)
23
+ * - Namespace: http://www.sap.com/adt/aunit (not http://www.sap.com/adt/api/aunit)
24
+ * - Objects via adtcore:objectReferences with URI (not aunit:tests with containerClass/class)
25
+ * - Content-Type/Accept: application/xml (not versioned vnd.sap.adt.api.abapunit.*)
23
26
  */
24
- async function startClassUnitTestRunLegacy(connection, tests, options) {
27
+ async function startClassUnitTestRunLegacy(connection, tests, _options) {
25
28
  if (!tests.length) {
26
29
  throw new Error('At least one test definition is required');
27
30
  }
28
- const scope = options?.scope ?? {
29
- ownTests: true,
30
- foreignTests: false,
31
- addForeignTestsAsPreview: true,
32
- };
33
- const risk = options?.riskLevel ?? {
34
- harmless: true,
35
- dangerous: true,
36
- critical: true,
37
- };
38
- const duration = options?.duration ?? {
39
- short: true,
40
- medium: true,
41
- long: true,
42
- };
43
- const testsXml = tests
44
- .map((test) => `<aunit:test containerClass="${(0, internalUtils_1.encodeSapObjectName)(test.containerClass).toUpperCase()}" class="${test.testClass}"/>`)
45
- .join('');
46
- const xml = `<?xml version="1.0" encoding="UTF-8"?><aunit:run xmlns:aunit="http://www.sap.com/adt/api/aunit" title="${options?.title || tests[0].testClass}" context="${options?.context || 'MCP ABAP ADT Client'}">
47
- <aunit:options>
48
- <aunit:scope ownTests="${boolAttr(scope.ownTests, true)}" foreignTests="${boolAttr(scope.foreignTests, false)}" addForeignTestsAsPreview="${boolAttr(scope.addForeignTestsAsPreview, true)}"/>
49
- <aunit:riskLevel harmless="${boolAttr(risk.harmless, true)}" dangerous="${boolAttr(risk.dangerous, true)}" critical="${boolAttr(risk.critical, true)}"/>
50
- <aunit:duration short="${boolAttr(duration.short, true)}" medium="${boolAttr(duration.medium, true)}" long="${boolAttr(duration.long, true)}"/>
51
- </aunit:options>
52
- <aunit:tests>
53
- ${testsXml}
54
- </aunit:tests>
55
- </aunit:run>`;
31
+ const objectRefs = tests
32
+ .map((test) => {
33
+ const className = (0, internalUtils_1.encodeSapObjectName)(test.containerClass).toLowerCase();
34
+ return ` <adtcore:objectReference adtcore:uri="/sap/bc/adt/oo/classes/${className}"/>`;
35
+ })
36
+ .join('\n');
37
+ const xml = `<?xml version="1.0" encoding="UTF-8"?><aunit:runConfiguration xmlns:aunit="http://www.sap.com/adt/aunit">
38
+ <external>
39
+ <coverage active="false"/>
40
+ </external>
41
+ <adtcore:objectSets xmlns:adtcore="http://www.sap.com/adt/core">
42
+ <objectSet kind="inclusive">
43
+ <adtcore:objectReferences>
44
+ ${objectRefs}
45
+ </adtcore:objectReferences>
46
+ </objectSet>
47
+ </adtcore:objectSets>
48
+ </aunit:runConfiguration>`;
56
49
  return connection.makeAdtRequest({
57
50
  url: '/sap/bc/adt/abapunit/testruns',
58
51
  method: 'POST',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/adt-clients",
3
- "version": "3.6.0",
3
+ "version": "3.7.0",
4
4
  "description": "ADT clients for SAP ABAP systems - AdtClient and AdtRuntimeClient",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",