@craftapit/tester
Version:
A focused, LLM-powered testing framework for natural language test scenarios
133 lines (132 loc) • 4.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.APIAdapter = void 0;
const BaseAdapter_1 = require("./BaseAdapter");
class APIAdapter extends BaseAdapter_1.BaseAdapter {
constructor(config) {
super(config);
this.lastResponse = null;
this.baseUrl = config.baseUrl || '';
this.defaultHeaders = {
'Content-Type': 'application/json',
...config.defaultHeaders
};
}
async initialize() {
console.log('Initializing API adapter');
// Nothing specific to initialize
}
async cleanup() {
console.log('Cleaning up API adapter');
// Nothing specific to clean up
}
async makeRequest(request) {
const url = this.resolveUrl(request.url);
const headers = { ...this.defaultHeaders, ...request.headers };
console.log(`Making ${request.method} request to ${url}`);
try {
const response = await fetch(url, {
method: request.method,
headers,
body: request.body ? JSON.stringify(request.body) : undefined
});
let responseBody;
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
responseBody = await response.json();
}
else {
responseBody = await response.text();
}
this.lastResponse = {
status: response.status,
headers: Object.fromEntries(response.headers.entries()),
body: responseBody
};
return this.lastResponse;
}
catch (error) {
console.error('API request failed:', error);
throw error;
}
}
async verifyResponse(expectations) {
if (!this.lastResponse) {
return {
success: false,
reason: 'No previous API response to verify'
};
}
// Verify status code
if (expectations.status && this.lastResponse.status !== expectations.status) {
return {
success: false,
reason: `Expected status ${expectations.status} but got ${this.lastResponse.status}`
};
}
// Verify body contains expected properties
if (expectations.bodyContains) {
for (const [key, value] of Object.entries(expectations.bodyContains)) {
if (!this.deepCompare(this.lastResponse.body, key, value)) {
return {
success: false,
reason: `Response body missing or has incorrect value for '${key}'`
};
}
}
}
// Verify body matches custom function
if (expectations.bodyMatches && !expectations.bodyMatches(this.lastResponse.body)) {
return {
success: false,
reason: 'Response body did not match custom validation function'
};
}
// Verify headers
if (expectations.headerContains) {
for (const [key, value] of Object.entries(expectations.headerContains)) {
const headerKey = Object.keys(this.lastResponse.headers)
.find(h => h.toLowerCase() === key.toLowerCase());
if (!headerKey || this.lastResponse.headers[headerKey] !== value) {
return {
success: false,
reason: `Expected header '${key}' with value '${value}' not found`
};
}
}
}
return { success: true };
}
getLastResponse() {
return this.lastResponse;
}
resolveUrl(path) {
if (path.startsWith('http://') || path.startsWith('https://')) {
return path;
}
let baseUrl = this.baseUrl;
if (baseUrl.endsWith('/') && path.startsWith('/')) {
baseUrl = baseUrl.slice(0, -1);
}
return `${baseUrl}${path}`;
}
deepCompare(obj, path, expectedValue) {
const parts = path.split('.');
let current = obj;
for (let i = 0; i < parts.length - 1; i++) {
if (current === undefined || current === null) {
return false;
}
current = current[parts[i]];
}
const lastPart = parts[parts.length - 1];
if (current === undefined || current === null) {
return false;
}
if (typeof expectedValue === 'object' && expectedValue !== null) {
return JSON.stringify(current[lastPart]) === JSON.stringify(expectedValue);
}
return current[lastPart] === expectedValue;
}
}
exports.APIAdapter = APIAdapter;