abapgit-agent 1.0.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.
- package/.abapGitAgent.example +11 -0
- package/API.md +271 -0
- package/CLAUDE.md +445 -0
- package/CLAUDE_MEM.md +88 -0
- package/ERROR_HANDLING.md +30 -0
- package/INSTALL.md +160 -0
- package/README.md +127 -0
- package/abap/CLAUDE.md +492 -0
- package/abap/package.devc.xml +10 -0
- package/abap/zcl_abgagt_agent.clas.abap +769 -0
- package/abap/zcl_abgagt_agent.clas.xml +15 -0
- package/abap/zcl_abgagt_cmd_factory.clas.abap +43 -0
- package/abap/zcl_abgagt_cmd_factory.clas.xml +15 -0
- package/abap/zcl_abgagt_command_inspect.clas.abap +192 -0
- package/abap/zcl_abgagt_command_inspect.clas.testclasses.abap +121 -0
- package/abap/zcl_abgagt_command_inspect.clas.xml +16 -0
- package/abap/zcl_abgagt_command_pull.clas.abap +80 -0
- package/abap/zcl_abgagt_command_pull.clas.testclasses.abap +87 -0
- package/abap/zcl_abgagt_command_pull.clas.xml +16 -0
- package/abap/zcl_abgagt_command_unit.clas.abap +297 -0
- package/abap/zcl_abgagt_command_unit.clas.xml +15 -0
- package/abap/zcl_abgagt_resource_health.clas.abap +25 -0
- package/abap/zcl_abgagt_resource_health.clas.xml +15 -0
- package/abap/zcl_abgagt_resource_inspect.clas.abap +62 -0
- package/abap/zcl_abgagt_resource_inspect.clas.xml +15 -0
- package/abap/zcl_abgagt_resource_pull.clas.abap +71 -0
- package/abap/zcl_abgagt_resource_pull.clas.xml +15 -0
- package/abap/zcl_abgagt_resource_unit.clas.abap +64 -0
- package/abap/zcl_abgagt_resource_unit.clas.xml +15 -0
- package/abap/zcl_abgagt_rest_handler.clas.abap +27 -0
- package/abap/zcl_abgagt_rest_handler.clas.xml +15 -0
- package/abap/zcl_abgagt_util.clas.abap +93 -0
- package/abap/zcl_abgagt_util.clas.testclasses.abap +84 -0
- package/abap/zcl_abgagt_util.clas.xml +16 -0
- package/abap/zif_abgagt_agent.intf.abap +134 -0
- package/abap/zif_abgagt_agent.intf.xml +15 -0
- package/abap/zif_abgagt_cmd_factory.intf.abap +7 -0
- package/abap/zif_abgagt_cmd_factory.intf.xml +15 -0
- package/abap/zif_abgagt_command.intf.abap +21 -0
- package/abap/zif_abgagt_command.intf.xml +15 -0
- package/abap/zif_abgagt_util.intf.abap +28 -0
- package/abap/zif_abgagt_util.intf.xml +15 -0
- package/bin/abapgit-agent +902 -0
- package/img/claude.png +0 -0
- package/package.json +31 -0
- package/scripts/claude-integration.js +351 -0
- package/scripts/test-integration.js +139 -0
- package/src/abap-client.js +314 -0
- package/src/agent.js +119 -0
- package/src/config.js +66 -0
- package/src/index.js +48 -0
- package/src/logger.js +39 -0
- package/src/server.js +116 -0
package/img/claude.png
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "abapgit-agent",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "ABAP Git Agent - Pull and activate ABAP code via abapGit from any git repository",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"abapgit-agent": "bin/abapgit-agent"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node src/server.js",
|
|
11
|
+
"dev": "nodemon src/server.js",
|
|
12
|
+
"test": "jest",
|
|
13
|
+
"pull": "node bin/abapgit-agent"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"cors": "^2.8.5",
|
|
17
|
+
"dotenv": "^16.3.1",
|
|
18
|
+
"express": "^4.18.2",
|
|
19
|
+
"node-fetch": "^2.7.0",
|
|
20
|
+
"uuid": "^9.0.0",
|
|
21
|
+
"winston": "^3.11.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"jest": "^29.7.0",
|
|
25
|
+
"nodemon": "^3.0.1",
|
|
26
|
+
"supertest": "^7.2.2"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=16.0.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Integration - Shell script for Claude Code
|
|
3
|
+
*
|
|
4
|
+
* This script can be run from any ABAP git repo with .abapGitAgent config.
|
|
5
|
+
*
|
|
6
|
+
* Usage in Claude:
|
|
7
|
+
* 1. Run: node scripts/claude-integration.js pull --url <git-url> [--branch <branch>]
|
|
8
|
+
* 2. Parse response for activation result
|
|
9
|
+
* 3. Display errors if any
|
|
10
|
+
*
|
|
11
|
+
* Alternatively, use the CLI directly:
|
|
12
|
+
* ./bin/abapgit-agent pull [--branch <branch>]
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const http = require('http');
|
|
16
|
+
const https = require('https');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
|
|
20
|
+
const COOKIE_FILE = path.join(__dirname, '..', '.abapgit_agent_cookies.txt');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if ABAP AI integration is configured for this repo
|
|
24
|
+
* Looks for .abapGitAgent in current working directory
|
|
25
|
+
*/
|
|
26
|
+
function isAbapIntegrationEnabled() {
|
|
27
|
+
// Check in current working directory (repo root)
|
|
28
|
+
const repoConfigPath = path.join(process.cwd(), '.abapGitAgent');
|
|
29
|
+
if (fs.existsSync(repoConfigPath)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
// Also check if repo has abap/ folder with ABAP objects
|
|
33
|
+
const abapFolder = path.join(process.cwd(), 'abap');
|
|
34
|
+
if (fs.existsSync(abapFolder)) {
|
|
35
|
+
const files = fs.readdirSync(abapFolder);
|
|
36
|
+
return files.some(f => f.endsWith('.abap') || f.endsWith('.clas.abap') || f.endsWith('.fugr.abap'));
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Load configuration from .abapGitAgent
|
|
43
|
+
*/
|
|
44
|
+
function loadConfig() {
|
|
45
|
+
// First check current working directory (repo root)
|
|
46
|
+
const repoConfigPath = path.join(process.cwd(), '.abapGitAgent');
|
|
47
|
+
|
|
48
|
+
if (fs.existsSync(repoConfigPath)) {
|
|
49
|
+
console.log(`📁 Found .abapGitAgent in: ${repoConfigPath}`);
|
|
50
|
+
return JSON.parse(fs.readFileSync(repoConfigPath, 'utf8'));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Fallback to abapgit-agent parent directory
|
|
54
|
+
const bridgeConfigPath = path.join(__dirname, '..', '.abapGitAgent');
|
|
55
|
+
if (fs.existsSync(bridgeConfigPath)) {
|
|
56
|
+
return JSON.parse(fs.readFileSync(bridgeConfigPath, 'utf8'));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Fallback to environment variables
|
|
60
|
+
return {
|
|
61
|
+
host: process.env.ABAP_HOST,
|
|
62
|
+
sapport: parseInt(process.env.ABAP_PORT, 10) || 443,
|
|
63
|
+
client: process.env.ABAP_CLIENT || '100',
|
|
64
|
+
user: process.env.ABAP_USER,
|
|
65
|
+
password: process.env.ABAP_PASSWORD,
|
|
66
|
+
language: process.env.ABAP_LANGUAGE || 'EN',
|
|
67
|
+
gitUsername: process.env.GIT_USERNAME,
|
|
68
|
+
gitPassword: process.env.GIT_PASSWORD
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Read cookies from Netscape format cookie file
|
|
74
|
+
*/
|
|
75
|
+
function readNetscapeCookies() {
|
|
76
|
+
if (!fs.existsSync(COOKIE_FILE)) return '';
|
|
77
|
+
|
|
78
|
+
const content = fs.readFileSync(COOKIE_FILE, 'utf8');
|
|
79
|
+
const lines = content.split('\n');
|
|
80
|
+
const cookies = [];
|
|
81
|
+
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
const trimmed = line.trim();
|
|
84
|
+
// Skip empty lines and header comments but NOT HttpOnly cookies
|
|
85
|
+
if (!trimmed || (trimmed.startsWith('#') && !trimmed.startsWith('#HttpOnly'))) continue;
|
|
86
|
+
|
|
87
|
+
const parts = trimmed.split('\t');
|
|
88
|
+
if (parts.length >= 7) {
|
|
89
|
+
cookies.push(`${parts[5]}=${parts[6]}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return cookies.join('; ');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Fetch CSRF token using GET /pull with X-CSRF-Token: fetch
|
|
98
|
+
*/
|
|
99
|
+
async function fetchCsrfToken(config) {
|
|
100
|
+
const url = new URL(`/sap/bc/z_abapgit_agent/pull`, `https://${config.host}:${config.sapport}`);
|
|
101
|
+
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
const cookieHeader = readNetscapeCookies();
|
|
104
|
+
|
|
105
|
+
const options = {
|
|
106
|
+
hostname: url.hostname,
|
|
107
|
+
port: url.port,
|
|
108
|
+
path: url.pathname,
|
|
109
|
+
method: 'GET',
|
|
110
|
+
headers: {
|
|
111
|
+
'Authorization': `Basic ${Buffer.from(`${config.user}:${config.password}`).toString('base64')}`,
|
|
112
|
+
'sap-client': config.client,
|
|
113
|
+
'sap-language': config.language || 'EN',
|
|
114
|
+
'X-CSRF-Token': 'fetch',
|
|
115
|
+
'Content-Type': 'application/json',
|
|
116
|
+
...(cookieHeader && { 'Cookie': cookieHeader })
|
|
117
|
+
},
|
|
118
|
+
agent: new https.Agent({ rejectUnauthorized: false })
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const req = https.request(options, (res) => {
|
|
122
|
+
const csrfToken = res.headers['x-csrf-token'];
|
|
123
|
+
|
|
124
|
+
// Save new cookies from response - the CSRF token is tied to this new session!
|
|
125
|
+
const setCookie = res.headers['set-cookie'];
|
|
126
|
+
if (setCookie) {
|
|
127
|
+
const cookies = Array.isArray(setCookie)
|
|
128
|
+
? setCookie.map(c => c.split(';')[0]).join('; ')
|
|
129
|
+
: setCookie.split(';')[0];
|
|
130
|
+
fs.writeFileSync(COOKIE_FILE, cookies);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let body = '';
|
|
134
|
+
res.on('data', chunk => body += chunk);
|
|
135
|
+
res.on('end', () => {
|
|
136
|
+
resolve(csrfToken);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
req.on('error', reject);
|
|
141
|
+
req.end();
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Make HTTP request to ABAP REST endpoint
|
|
147
|
+
*/
|
|
148
|
+
function request(method, path, data = null, options = {}) {
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
const config = loadConfig();
|
|
151
|
+
const url = new URL(path, `https://${config.host}:${config.sapport}`);
|
|
152
|
+
|
|
153
|
+
const headers = {
|
|
154
|
+
'Content-Type': 'application/json',
|
|
155
|
+
'sap-client': config.client,
|
|
156
|
+
'sap-language': config.language || 'EN',
|
|
157
|
+
...options.headers
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Add authorization
|
|
161
|
+
headers['Authorization'] = `Basic ${Buffer.from(`${config.user}:${config.password}`).toString('base64')}`;
|
|
162
|
+
|
|
163
|
+
// Add CSRF token for POST
|
|
164
|
+
if (method === 'POST' && options.csrfToken) {
|
|
165
|
+
headers['X-CSRF-Token'] = options.csrfToken;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Add cookies if available
|
|
169
|
+
const cookieHeader = readNetscapeCookies();
|
|
170
|
+
if (cookieHeader) {
|
|
171
|
+
headers['Cookie'] = cookieHeader;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const reqOptions = {
|
|
175
|
+
hostname: url.hostname,
|
|
176
|
+
port: url.port,
|
|
177
|
+
path: url.pathname,
|
|
178
|
+
method,
|
|
179
|
+
headers,
|
|
180
|
+
agent: new https.Agent({ rejectUnauthorized: false })
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const req = (url.protocol === 'https:' ? https : http).request(reqOptions, (res) => {
|
|
184
|
+
let body = '';
|
|
185
|
+
res.on('data', chunk => body += chunk);
|
|
186
|
+
res.on('end', () => {
|
|
187
|
+
try {
|
|
188
|
+
resolve(JSON.parse(body));
|
|
189
|
+
} catch (e) {
|
|
190
|
+
resolve(body);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
req.on('error', reject);
|
|
196
|
+
|
|
197
|
+
if (data) {
|
|
198
|
+
req.write(JSON.stringify(data));
|
|
199
|
+
}
|
|
200
|
+
req.end();
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Pull and activate repository
|
|
206
|
+
*/
|
|
207
|
+
async function pull(gitUrl, branch = 'main') {
|
|
208
|
+
console.log(`\n🚀 Starting pull for: ${gitUrl}`);
|
|
209
|
+
console.log(` Branch: ${branch}`);
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const config = loadConfig();
|
|
213
|
+
|
|
214
|
+
// Fetch CSRF token first
|
|
215
|
+
const csrfToken = await fetchCsrfToken(config);
|
|
216
|
+
|
|
217
|
+
// Prepare request data with git credentials
|
|
218
|
+
const data = {
|
|
219
|
+
url: gitUrl,
|
|
220
|
+
branch: branch,
|
|
221
|
+
username: config.gitUsername,
|
|
222
|
+
password: config.gitPassword
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const result = await request('POST', '/sap/bc/z_abapgit_agent/pull', data, { csrfToken });
|
|
226
|
+
|
|
227
|
+
console.log('\n');
|
|
228
|
+
|
|
229
|
+
if (result.success === 'X' || result.success === true) {
|
|
230
|
+
console.log(`✅ Pull completed successfully!`);
|
|
231
|
+
console.log(` Job ID: ${result.job_id}`);
|
|
232
|
+
console.log(` Message: ${result.message}`);
|
|
233
|
+
} else {
|
|
234
|
+
console.log(`❌ Pull completed with errors!`);
|
|
235
|
+
console.log(` Job ID: ${result.job_id}`);
|
|
236
|
+
console.log(` Message: ${result.message}`);
|
|
237
|
+
|
|
238
|
+
if (result.error_detail) {
|
|
239
|
+
console.log(`\n📋 Error Details:`);
|
|
240
|
+
console.log(result.error_detail);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return result;
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.error(`\n❌ Error: ${error.message}`);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Check agent health
|
|
253
|
+
*/
|
|
254
|
+
async function healthCheck() {
|
|
255
|
+
try {
|
|
256
|
+
const result = await request('GET', '/sap/bc/z_abapgit_agent/health');
|
|
257
|
+
return result;
|
|
258
|
+
} catch (error) {
|
|
259
|
+
return { status: 'unreachable', error: error.message };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Main CLI
|
|
265
|
+
*/
|
|
266
|
+
async function main() {
|
|
267
|
+
const args = process.argv.slice(2);
|
|
268
|
+
const command = args[0];
|
|
269
|
+
|
|
270
|
+
// Check if ABAP integration is enabled for this repo
|
|
271
|
+
if (!isAbapIntegrationEnabled()) {
|
|
272
|
+
console.log(`
|
|
273
|
+
⚠️ ABAP AI Integration not configured for this repository.
|
|
274
|
+
|
|
275
|
+
To enable integration:
|
|
276
|
+
1. Create a .abapGitAgent file in the repo root with ABAP connection details:
|
|
277
|
+
{
|
|
278
|
+
"host": "your-sap-system.com",
|
|
279
|
+
"sapport": 443,
|
|
280
|
+
"client": "100",
|
|
281
|
+
"user": "TECH_USER",
|
|
282
|
+
"password": "your-password",
|
|
283
|
+
"language": "EN"
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
2. Or set environment variables:
|
|
287
|
+
- ABAP_HOST, ABAP_PORT, ABAP_CLIENT, ABAP_USER, ABAP_PASSWORD
|
|
288
|
+
`);
|
|
289
|
+
if (command !== 'help' && command !== '--help' && command !== '-h') {
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
switch (command) {
|
|
296
|
+
case 'pull':
|
|
297
|
+
const urlIndex = args.indexOf('--url') !== -1 ? args.indexOf('--url') + 1 : 1;
|
|
298
|
+
const branchIndex = args.indexOf('--branch');
|
|
299
|
+
|
|
300
|
+
if (!args[urlIndex]) {
|
|
301
|
+
console.error('Error: --url is required');
|
|
302
|
+
console.error('Usage: node scripts/claude-integration.js pull --url <git-url> [--branch <branch>]');
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
await pull(args[urlIndex], branchIndex !== -1 ? args[branchIndex + 1] : 'main');
|
|
307
|
+
break;
|
|
308
|
+
|
|
309
|
+
case 'health':
|
|
310
|
+
const health = await healthCheck();
|
|
311
|
+
console.log(JSON.stringify(health, null, 2));
|
|
312
|
+
break;
|
|
313
|
+
|
|
314
|
+
case 'status':
|
|
315
|
+
if (isAbapIntegrationEnabled()) {
|
|
316
|
+
console.log('✅ ABAP AI Integration is ENABLED');
|
|
317
|
+
console.log(' Config location:', path.join(process.cwd(), '.abapGitAgent'));
|
|
318
|
+
} else {
|
|
319
|
+
console.log('❌ ABAP AI Integration is NOT configured');
|
|
320
|
+
}
|
|
321
|
+
break;
|
|
322
|
+
|
|
323
|
+
default:
|
|
324
|
+
console.log(`
|
|
325
|
+
ABAP AI Bridge - Claude Integration
|
|
326
|
+
|
|
327
|
+
Usage:
|
|
328
|
+
node scripts/claude-integration.js <command> [options]
|
|
329
|
+
|
|
330
|
+
Commands:
|
|
331
|
+
pull --url <git-url> [--branch <branch>]
|
|
332
|
+
Pull and activate a repository in ABAP system
|
|
333
|
+
|
|
334
|
+
health
|
|
335
|
+
Check if ABAP REST API is healthy
|
|
336
|
+
|
|
337
|
+
status
|
|
338
|
+
Check if ABAP integration is configured for this repo
|
|
339
|
+
|
|
340
|
+
Examples:
|
|
341
|
+
node scripts/claude-integration.js pull --url https://github.com/user/repo --branch main
|
|
342
|
+
node scripts/claude-integration.js health
|
|
343
|
+
`);
|
|
344
|
+
}
|
|
345
|
+
} catch (error) {
|
|
346
|
+
console.error(`Error: ${error.message}`);
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
main();
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple integration test for Claude
|
|
3
|
+
* Run: node scripts/test-integration.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const http = require('http');
|
|
7
|
+
const https = require('https');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Mock ABAP response for testing without actual ABAP system
|
|
11
|
+
*/
|
|
12
|
+
function mockAbapResponse(success = true) {
|
|
13
|
+
if (success) {
|
|
14
|
+
return {
|
|
15
|
+
success: 'X',
|
|
16
|
+
job_id: 'TEST123_20260206_120000',
|
|
17
|
+
message: 'Pull completed successfully',
|
|
18
|
+
error_detail: null
|
|
19
|
+
};
|
|
20
|
+
} else {
|
|
21
|
+
return {
|
|
22
|
+
success: '',
|
|
23
|
+
job_id: 'TEST123_20260206_120000',
|
|
24
|
+
message: 'Pull completed with errors',
|
|
25
|
+
error_detail: 'Errors/Warnings:\n - CLAS ZCL_TEST: Syntax error in line 15\n - PROG ZTEST_REPORT: Unknown variable'
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Test the response parsing logic
|
|
32
|
+
*/
|
|
33
|
+
function testResponseParsing() {
|
|
34
|
+
console.log('\n🧪 Testing Response Parsing...\n');
|
|
35
|
+
|
|
36
|
+
// Test success response
|
|
37
|
+
const successResponse = mockAbapResponse(true);
|
|
38
|
+
console.log('✅ Success Response:');
|
|
39
|
+
console.log(JSON.stringify(successResponse, null, 2));
|
|
40
|
+
|
|
41
|
+
const successResult = {
|
|
42
|
+
success: successResponse.success === 'X' || successResponse.success === true,
|
|
43
|
+
job_id: successResponse.job_id,
|
|
44
|
+
message: successResponse.message,
|
|
45
|
+
error_detail: successResponse.error_detail
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
console.log('\nParsed:');
|
|
49
|
+
console.log(JSON.stringify(successResult, null, 2));
|
|
50
|
+
|
|
51
|
+
// Test error response
|
|
52
|
+
const errorResponse = mockAbapResponse(false);
|
|
53
|
+
console.log('\n❌ Error Response:');
|
|
54
|
+
console.log(JSON.stringify(errorResponse, null, 2));
|
|
55
|
+
|
|
56
|
+
const errorResult = {
|
|
57
|
+
success: errorResponse.success === 'X' || errorResponse.success === true,
|
|
58
|
+
job_id: errorResponse.job_id,
|
|
59
|
+
message: errorResponse.message,
|
|
60
|
+
error_detail: errorResponse.error_detail
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
console.log('\nParsed:');
|
|
64
|
+
console.log(JSON.stringify(errorResult, null, 2));
|
|
65
|
+
|
|
66
|
+
// Verify parsing
|
|
67
|
+
if (successResult.success === true && errorResult.success === false) {
|
|
68
|
+
console.log('\n✅ Response parsing works correctly!');
|
|
69
|
+
return true;
|
|
70
|
+
} else {
|
|
71
|
+
console.log('\n❌ Response parsing failed!');
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Test URL construction
|
|
78
|
+
*/
|
|
79
|
+
function testUrlConstruction() {
|
|
80
|
+
console.log('\n🧪 Testing URL Construction...\n');
|
|
81
|
+
|
|
82
|
+
const config = {
|
|
83
|
+
host: 'your-sap-system.com',
|
|
84
|
+
sapport: 44300,
|
|
85
|
+
client: '100',
|
|
86
|
+
user: 'TECH_USER',
|
|
87
|
+
password: 'secret',
|
|
88
|
+
language: 'EN'
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const expectedUrl = `https://${config.host}:${config.sapport}/sap/bc/z_abapgit_agent/pull`;
|
|
92
|
+
console.log(`Expected URL: ${expectedUrl}`);
|
|
93
|
+
console.log('✅ URL construction logic is correct');
|
|
94
|
+
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Test error detail formatting
|
|
100
|
+
*/
|
|
101
|
+
function testErrorDetailFormatting() {
|
|
102
|
+
console.log('\n🧪 Testing Error Detail Formatting...\n');
|
|
103
|
+
|
|
104
|
+
const errorDetail = `Errors/Warnings:
|
|
105
|
+
- CLAS ZCL_TEST: Syntax error in line 15
|
|
106
|
+
- PROG ZTEST_REPORT: Unknown variable`;
|
|
107
|
+
|
|
108
|
+
console.log('Error Detail Output:');
|
|
109
|
+
console.log(errorDetail);
|
|
110
|
+
console.log('\n✅ Error formatting works correctly');
|
|
111
|
+
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Main test runner
|
|
117
|
+
*/
|
|
118
|
+
function main() {
|
|
119
|
+
console.log('='.repeat(50));
|
|
120
|
+
console.log('ABAP AI Bridge - Integration Tests');
|
|
121
|
+
console.log('='.repeat(50));
|
|
122
|
+
|
|
123
|
+
const results = [];
|
|
124
|
+
|
|
125
|
+
results.push(testResponseParsing());
|
|
126
|
+
results.push(testUrlConstruction());
|
|
127
|
+
results.push(testErrorDetailFormatting());
|
|
128
|
+
|
|
129
|
+
console.log('\n' + '='.repeat(50));
|
|
130
|
+
if (results.every(r => r === true)) {
|
|
131
|
+
console.log('✅ All tests passed!');
|
|
132
|
+
process.exit(0);
|
|
133
|
+
} else {
|
|
134
|
+
console.log('❌ Some tests failed!');
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
main();
|