Skip to content

Commit

Permalink
change to fetch for the property tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bdemann committed Jan 29, 2024
1 parent 489e545 commit 61fc534
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 108 deletions.
18 changes: 13 additions & 5 deletions property_tests/arbitraries/http/request_arb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const RequestMethodArb = fc.constantFrom<RequestMethod>(
'PUT',
'DELETE',
'OPTIONS',
'TRACE',
// 'TRACE',
'PATCH'
);

Expand Down Expand Up @@ -65,15 +65,23 @@ function HttpRequestValueArb() {
return optCertVer === null ? None : Some(optCertVer);
})
)
.map(([method, url, headers, body, certificate_version]) => {
return {
.map(
([
method,
url,
headers,
body,
certificate_version
};
});
]): HttpRequest => {
return {
method,
url,
headers,
body,
certificate_version
};
}
);
}

export function HttpRequestArb(): fc.Arbitrary<
Expand Down
68 changes: 26 additions & 42 deletions property_tests/tests/canister_methods/http_request/test/fletch.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,40 @@
import { execSync } from 'child_process';
import { HttpRequest } from 'azle';
import { getCanisterId } from 'azle/test';
import * as dns from 'node:dns';
dns.setDefaultResultOrder('ipv4first');

type HttpResponse = {
status: number;
headers: string[][];
body: string;
};

/**
* A synchronous "fetch" for canisters.
* An asynchronous "fetch" for canisters.
*/
export function fletch(canisterName: string, options: HttpRequest) {
export async function fletch(
canisterName: string,
request: HttpRequest
): Promise<HttpResponse> {
const canisterId = getCanisterId(canisterName);

const requestHeaders = toCurlHeadersString(options.headers);

const curlCommand = `curl\
--silent\
--include\
-X ${options.method}\
${requestHeaders}\
--data "${options.body.join(',')}"\
"${canisterId}.localhost:8000${options.url}" \
--resolve "${canisterId}.localhost:8000:127.0.0.1"`;
const { method, body, headers, url: path } = request;

const responseBuffer = execSync(curlCommand);

return toResponse(responseBuffer);
}
const url = `http://${canisterId}.localhost:8000${path}`;

function toCurlHeadersString(headers: [string, string][]) {
return headers
.map(([name, value]) => `-H ${singleQuotedString(`${name}: ${value}`)}`)
.join(' ');
}

function singleQuotedString(input: string) {
const singleQuoteEscapedString = input.replace(/'/g, "'\\''");
const fetchOptions = {
method,
headers,
body: method === 'GET' ? undefined : body
};

return `'${singleQuoteEscapedString}'`;
return await toResponse(await fetch(url, fetchOptions));
}

function toResponse(buffer: Buffer) {
const responseString = new TextDecoder().decode(buffer);

const [statusCodeAndHeaders, body] = responseString.split('\r\n\r\n');

const [statusCodeString, ...responseHeaderStrings] =
statusCodeAndHeaders.split('\r\n');

const status = Number(statusCodeString.split(' ')[1]);
async function toResponse(response: Response): Promise<HttpResponse> {
const headers: [string, string][] = Array.from(response.headers.entries());
const status = response.status;
const body = await response.text();

const headers = responseHeaderStrings.map((header) => header.split(': '));

return {
status,
headers,
body
};
return { status, headers, body };
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function generateTests(
{
name: functionName,
test: async () => {
const response = fletch('canister', request);
const response = await fletch('canister', request);
const filteredHeaders = response.headers
.filter(
([name]) =>
Expand All @@ -36,12 +36,11 @@ export function generateTests(
headers: filteredHeaders,
body: response.body
};
const filteredExpectedHeaders =
const sortedExpectedHeaders =
expectedResponse.headers.sort();
const processedExpectedResponse = {
status: expectedResponse.status,
headers: filteredExpectedHeaders,
body: expectedResponse.body
...expectedResponse,
headers: sortedExpectedHeaders
};
const valuesAreEqual = deepEqual(
processedResponse,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,56 +1,40 @@
import { execSync } from 'child_process';
import { HttpRequest } from 'azle';
import { getCanisterId } from 'azle/test';
import * as dns from 'node:dns';
dns.setDefaultResultOrder('ipv4first');

type HttpResponse = {
status: number;
headers: string[][];
body: string;
};

/**
* A synchronous "fetch" for canisters.
* An asynchronous "fetch" for canisters.
*/
export function fletch(canisterName: string, options: HttpRequest) {
export async function fletch(
canisterName: string,
request: HttpRequest
): Promise<HttpResponse> {
const canisterId = getCanisterId(canisterName);

const requestHeaders = toCurlHeadersString(options.headers);

const curlCommand = `curl\
--silent\
--include\
-X ${options.method}\
${requestHeaders}\
--data "${options.body.join(',')}"\
"${canisterId}.localhost:8000${options.url}" \
--resolve "${canisterId}.localhost:8000:127.0.0.1"`;
const { method, body, headers, url: path } = request;

const responseBuffer = execSync(curlCommand);

return toResponse(responseBuffer);
}
const url = `http://${canisterId}.localhost:8000${path}`;

function toCurlHeadersString(headers: [string, string][]) {
return headers
.map(([name, value]) => `-H ${singleQuotedString(`${name}: ${value}`)}`)
.join(' ');
}

function singleQuotedString(input: string) {
const singleQuoteEscapedString = input.replace(/'/g, "'\\''");
const fetchOptions = {
method,
headers,
body: method === 'GET' ? undefined : body
};

return `'${singleQuoteEscapedString}'`;
return await toResponse(await fetch(url, fetchOptions));
}

function toResponse(buffer: Buffer) {
const responseString = new TextDecoder().decode(buffer);

const [statusCodeAndHeaders, body] = responseString.split('\r\n\r\n');

const [statusCodeString, ...responseHeaderStrings] =
statusCodeAndHeaders.split('\r\n');

const status = Number(statusCodeString.split(' ')[1]);
async function toResponse(response: Response): Promise<HttpResponse> {
const headers: [string, string][] = Array.from(response.headers.entries());
const status = response.status;
const body = await response.text();

const headers = responseHeaderStrings.map((header) => header.split(': '));

return {
status,
headers,
body
};
return { status, headers, body };
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,12 @@ function generateHeadersMap(
}

function generateHeaderChecks(headers: [string, string][]) {
return headers
.filter(([_, value]) => value !== '') // An empty header value is the same as a none existent header
.map(
([name, value]) => `
return (
headers
.filter(([_, value]) => value !== '') // An empty header value is the same as a none existent header
// TODO wouldn't it be great instead of throwing away that test to actually verify that any empty values are undefined?
.map(
([name, value]) => `
if (headers['${escape(name).toLowerCase()}'] !== '${escape(
value
)}') {
Expand All @@ -85,8 +87,9 @@ function generateHeaderChecks(headers: [string, string][]) {
);
}
`
)
.join('\n');
)
.join('\n')
);
}

function escape(input: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,30 @@ export function generateTests(
{
name: functionName,
test: async () => {
const response = fletch('canister', request);
const filteredHeaders = response.headers.filter(
([name]) =>
name !== 'x-ic-streaming-response' &&
name !== 'content-length' &&
name !== 'date'
);
const response = await fletch('canister', request);

const filteredHeaders = response.headers
.filter(
([name]) =>
name !== 'x-ic-streaming-response' &&
name !== 'content-length' &&
name !== 'date'
)
.sort();
const sortedExpectedHeaders =
expectedResponse.headers.sort();
const processedResponse = {
status: response.status,
headers: filteredHeaders,
body: response.body
};
const processedExpectedResponse = {
...expectedResponse,
headers: sortedExpectedHeaders
};
const valuesAreEqual = deepEqual(
processedResponse,
expectedResponse
processedExpectedResponse
);

return { Ok: valuesAreEqual };
Expand Down

0 comments on commit 61fc534

Please sign in to comment.