Skip to content

Commit 9a6554b

Browse files
committed
move all requests to backend
1 parent e998163 commit 9a6554b

File tree

7 files changed

+161
-502
lines changed

7 files changed

+161
-502
lines changed

src/commands/export.js

Lines changed: 34 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,28 @@ const {
66
PYTHAGORA_METADATA_DIR,
77
METADATA_FILENAME,
88
EXPORT_METADATA_FILENAME,
9-
SRC_TO_ROOT,
10-
MIN_TOKENS_FOR_GPT_RESPONSE,
11-
MAX_GPT_MODEL_TOKENS
9+
SRC_TO_ROOT
1210
} = require('../const/common');
1311
const {getAllGeneratedTests, updateMetadata} = require("../utils/common");
1412
const {convertOldTestForGPT} = require("../utils/legacy");
15-
const {getJestTestFromPythagoraData, getJestTestName, getJestAuthFunction, getTokensInMessages, getPromptFromFile} = require("../helpers/openai");
1613
const {setUpPythagoraDirs} = require("../helpers/starting");
17-
const {testExported, pleaseCaptureLoginTestLog, enterLoginRouteLog, testEligibleForExportLog} = require("../utils/cmdPrint");
14+
const {
15+
logAndExit,
16+
testExported,
17+
pleaseCaptureLoginTestLog,
18+
enterLoginRouteLog,
19+
testEligibleForExportLog,
20+
jestAuthFileGenerationLog
21+
} = require("../utils/cmdPrint");
22+
const {
23+
getJestAuthFunction,
24+
getJestTest,
25+
getJestTestName,
26+
isEligibleForExport,
27+
cleanupGPTResponse
28+
} = require("../helpers/api");
1829
const _ = require('lodash');
1930
const args = require('../utils/getArgs.js');
20-
const {logAndExit} = require("@pythagora.io/pythagora-dev/src/utils/cmdPrint");
2131

2232
async function createDefaultFiles(generatedTests) {
2333
if (!fs.existsSync('jest.config.js')) {
@@ -68,14 +78,6 @@ function configurePrepareDbFile() {
6878
// TODO
6979
}
7080

71-
function cleanupGPTResponse(gptResponse) {
72-
if (gptResponse.substring(0, 3) === "```") {
73-
gptResponse = gptResponse.substring(gptResponse.indexOf('\n') + 2, gptResponse.lastIndexOf('```'));
74-
}
75-
76-
return gptResponse;
77-
}
78-
7981
function cleanupDataFolder() {
8082
const pythagoraTestsFolderPath = `./${EXPORTED_TESTS_DIR}`;
8183
const dataFolderPath = `./${EXPORTED_TESTS_DATA_DIR}`;
@@ -99,19 +101,16 @@ function cleanupDataFolder() {
99101
}
100102
}
101103

102-
async function exportTest(originalTest, usedNames) {
103-
// TODO remove in the future
104+
async function exportTest(originalTest, exportsMetadata) {
104105
let test = convertOldTestForGPT(originalTest);
105-
106-
let gptResponse = await getJestTestFromPythagoraData(test);
107-
let jestTest = cleanupGPTResponse(gptResponse);
108-
109-
let testName = await getJestTestName(jestTest, usedNames);
106+
let jestTest = await getJestTest(test);
107+
let testName = await getJestTestName(jestTest, Object.values(exportsMetadata).map(obj => obj.testName));
110108
fs.writeFileSync(`./${EXPORTED_TESTS_DATA_DIR}/${testName.replace('.test.js', '.json')}`, JSON.stringify(test.mongoQueries, null, 2));
111109
fs.writeFileSync(`./${EXPORTED_TESTS_DIR}/${testName}`, jestTest.replace(test.testId, testName));
112110

113111
testExported(testName);
114-
return testName;
112+
saveExportJson(exportsMetadata, originalTest, testName);
113+
115114
}
116115

117116
function testExists(exportsMetadata, testId) {
@@ -137,36 +136,25 @@ function saveExportJson(exportsMetadata, test, testName) {
137136
if (testId) {
138137
if (testExists(exportsMetadata, testId)) logAndExit(`Test with id ${testId} already generated, you can find it here: ${`./${EXPORTED_TESTS_DIR}/${exportsMetadata[testId].testName}`}. If you want to generate it again delete old one first.`);
139138

140-
let test = generatedTests.find(t => t.id === testId);
141-
if (!test) throw new Error(`Test with id ${testId} not found`);
139+
let originalTest = generatedTests.find(t => t.id === testId);
140+
if (!originalTest) throw new Error(`Test with id ${testId} not found`);
142141

143-
let testName = await exportTest(test, Object.values(exportsMetadata).map(obj => obj.testName));
144-
saveExportJson(exportsMetadata, test, testName);
142+
await exportTest(originalTest, exportsMetadata);
145143
}
146144
else {
147-
for (let test of generatedTests) {
148-
if (test.method === 'OPTIONS') continue;
149-
if (testExists(exportsMetadata, test.id)) {
150-
console.log(`Test with id ${test.id} already generated, you can find it here: ${`./${EXPORTED_TESTS_DIR}/${exportsMetadata[test.id].testName}`}.`);
145+
for (let originalTest of generatedTests) {
146+
if (originalTest.method === 'OPTIONS') continue;
147+
if (testExists(exportsMetadata, originalTest.id)) {
148+
console.log(`Test with id ${originalTest.id} already generated, you can find it here: ${`./${EXPORTED_TESTS_DIR}/${exportsMetadata[originalTest.id].testName}`}.`);
151149
continue;
152150
}
153151

154-
let testData = convertOldTestForGPT(test);
155-
let tokens = getTokensInMessages([
156-
{"role": "system", "content": "You are a QA engineer and your main goal is to find ways to break the application you're testing. You are proficient in writing automated integration tests for Node.js API servers.\n" +
157-
"When you respond, you don't say anything except the code - no formatting, no explanation - only code.\n" },
158-
{
159-
"role": "user",
160-
"content": getPromptFromFile('generateJestTest.txt', { testData }),
161-
},
162-
]);
163-
164-
let isEligibleForExport = (tokens + MIN_TOKENS_FOR_GPT_RESPONSE < MAX_GPT_MODEL_TOKENS);
165-
166-
if (isEligibleForExport) {
167-
let testName = await exportTest(test, Object.values(exportsMetadata).map(obj => obj.testName));
168-
saveExportJson(exportsMetadata, test, testName);
169-
} else testEligibleForExportLog(test.endpoint, test.id, isEligibleForExport);
152+
let test = convertOldTestForGPT(originalTest);
153+
const isEligible = await isEligibleForExport(test);
154+
155+
if (isEligible) {
156+
await exportTest(originalTest, exportsMetadata);
157+
} else testEligibleForExportLog(originalTest.endpoint, originalTest.id, isEligible);
170158
}
171159
}
172160

src/helpers/api.js

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
const http = require('http');
2+
const _ = require('lodash');
3+
const axios = require('axios');
4+
const {} = require('../utils/cmdPrint');
5+
6+
function extractGPTMessageFromStreamData(input) {
7+
const regex = /data: (.*?)\n/g;
8+
const substrings = [];
9+
let match;
10+
11+
while ((match = regex.exec(input)) !== null) {
12+
substrings.push(match[1]);
13+
}
14+
15+
return substrings.map(s => JSON.parse(s));
16+
}
17+
18+
function setOptions({protocol, hostname, port, path, method, headers}) {
19+
let options = {
20+
protocol: protocol || 'http',
21+
hostname: hostname || 'localhost',
22+
port: port || process.env.PORT,
23+
path: path || '/',
24+
method: method || 'POST',
25+
headers: headers || {
26+
'Content-Type': 'application/json',
27+
'Authorization': 'Bearer ' + process.env.OPENAI_API_KEY
28+
},
29+
};
30+
31+
if (!options.port) delete options.port;
32+
return options
33+
}
34+
35+
async function makeRequest(data, options) {
36+
let gptResponse = '';
37+
38+
return new Promise((resolve, reject) => {
39+
const req = http.request(_.omit(options, ['protocol']), function(res){
40+
res.on('data', (chunk) => {
41+
try {
42+
let receivedMessages = extractGPTMessageFromStreamData(chunk.toString());
43+
receivedMessages.forEach(rm => {
44+
let content = _.get(rm, 'choices.0.delta.content');
45+
if (content) {
46+
gptResponse += content;
47+
process.stdout.write(content);
48+
}
49+
});
50+
51+
} catch (e) {}
52+
});
53+
res.on('end', async () => {
54+
gptResponse = cleanupGPTResponse(gptResponse);
55+
resolve(gptResponse);
56+
});
57+
});
58+
59+
req.on('error', (e) => {
60+
console.error("problem with request:"+e.message);
61+
reject(e);
62+
});
63+
64+
req.write(data);
65+
66+
req.end();
67+
});
68+
}
69+
70+
async function getJestAuthFunction(loginMongoQueriesArray, loginRequestBody, loginEndpointPath) {
71+
jestAuthFileGenerationLog();
72+
73+
let options = setOptions({path: '/generate-jest-auth'});
74+
return await makeRequest(JSON.stringify({loginMongoQueriesArray, loginRequestBody, loginEndpointPath}), options);
75+
}
76+
77+
78+
async function getJestTest(test) {
79+
let options = setOptions({path: '/generate-jest-test'});
80+
return await makeRequest(JSON.stringify(test), options);
81+
}
82+
83+
async function getJestTestName(jestTest, usedNames) {
84+
let options = setOptions({path:'/generate-jest-test-name'});
85+
return await makeRequest(JSON.stringify({test: jestTest}), options);
86+
}
87+
88+
async function isEligibleForExport(jestTest) {
89+
try {
90+
let options = setOptions({ path: '/check-if-eligible' });
91+
92+
const response = await axios.post(
93+
`${options.protocol}://${options.hostname}:${options.port}${options.path}`,
94+
JSON.stringify({ jestTest }),
95+
{ headers: options.headers }
96+
);
97+
98+
return response.data;
99+
} catch (error) {
100+
console.log(error);
101+
return false;
102+
}
103+
}
104+
105+
function cleanupGPTResponse(gptResponse) {
106+
if (gptResponse.substring(0, 3) === "```") {
107+
gptResponse = gptResponse.substring(gptResponse.indexOf('\n') + 2, gptResponse.lastIndexOf('```'));
108+
}
109+
110+
return gptResponse;
111+
}
112+
113+
module.exports = {
114+
getJestAuthFunction,
115+
getJestTest,
116+
getJestTestName,
117+
isEligibleForExport,
118+
cleanupGPTResponse
119+
}

0 commit comments

Comments
 (0)