0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-04-07 23:41:43 -05:00

fix(NodeApp): fix responses with null bodies never completing (#9931)

* fix(NodeApp): fix responses with null bodies never completing

* add changeset

* add test

* chore(tests): restore correct assertions

* adjust incorrect test

* added Astro.redirect and Response.redirect test cases

* updated incorrect HTTP status

* adjust api-routes.test.js after cherry-pick

* bup markdoc test timeout

---------

Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>
Co-authored-by: Friedemann Sommer <friedemannsommer@users.noreply.github.com>
This commit is contained in:
Arsh 2024-02-01 21:24:21 +00:00 committed by GitHub
parent 879c712c03
commit 4d23caed76
6 changed files with 81 additions and 18 deletions

View file

@ -1,12 +1,16 @@
import nodejs from '../dist/index.js';
import { loadFixture, createRequestAndResponse } from './test-utils.js';
import crypto from 'node:crypto';
import { describe, it, before } from 'node:test';
import { describe, it, before, after } from 'node:test';
import * as assert from 'node:assert/strict';
describe('API routes', () => {
/** @type {import('./test-utils').Fixture} */
let fixture;
/** @type {import('astro/src/@types/astro.js').PreviewServer} */
let previewServer;
/** @type {URL} */
let baseUri;
before(async () => {
fixture = await loadFixture({
@ -15,8 +19,12 @@ describe('API routes', () => {
adapter: nodejs({ mode: 'middleware' }),
});
await fixture.build();
previewServer = await fixture.preview();
baseUri = new URL(`http://${previewServer.host ?? 'localhost'}:${previewServer.port}/`);
});
after(() => previewServer.stop());
it('Can get the request body', async () => {
const { handler } = await import('./fixtures/api-route/dist/server/entry.mjs');
let { req, res, done } = createRequestAndResponse({
@ -109,4 +117,37 @@ describe('API routes', () => {
assert.deepEqual(locals, { cancelledByTheServer: true });
});
it('Can respond with SSR redirect', async () => {
const controller = new AbortController();
setTimeout(() => controller.abort(), 1000);
const response = await fetch(new URL('/redirect', baseUri), {
redirect: 'manual',
signal: controller.signal,
});
assert.equal(response.status, 302);
assert.equal(response.headers.get('location'), '/destination');
});
it('Can respond with Astro.redirect', async () => {
const controller = new AbortController();
setTimeout(() => controller.abort(), 1000);
const response = await fetch(new URL('/astro-redirect', baseUri), {
redirect: 'manual',
signal: controller.signal,
});
assert.equal(response.status, 303);
assert.equal(response.headers.get('location'), '/destination');
});
it('Can respond with Response.redirect', async () => {
const controller = new AbortController();
setTimeout(() => controller.abort(), 1000);
const response = await fetch(new URL('/response-redirect', baseUri), {
redirect: 'manual',
signal: controller.signal,
});
assert.equal(response.status, 307);
assert.equal(response.headers.get('location'), String(new URL('/destination', baseUri)));
});
});

View file

@ -32,16 +32,25 @@ describe('Errors', () => {
});
it('generator that throws called in template', async () => {
const result = ['<!DOCTYPE html><h1>Astro</h1> 1', 'Internal server error'];
/** @type {Response} */
const res = await fixture.fetch('/generator');
const reader = res.body.getReader();
const decoder = new TextDecoder();
const expect = async ({ done, value }) => {
const result = await reader.read();
assert.equal(result.done, done);
if (!done) assert.equal(decoder.decode(result.value), value);
};
await expect({ done: false, value: '<!DOCTYPE html><h1>Astro</h1> 1Internal server error' });
await expect({ done: true });
const chunk1 = await reader.read();
const chunk2 = await reader.read();
const chunk3 = await reader.read();
assert.equal(chunk1.done, false);
if (chunk2.done) {
assert.equal(decoder.decode(chunk1.value), result.join(""));
}
else if (chunk3.done) {
assert.equal(decoder.decode(chunk1.value), result[0]);
assert.equal(decoder.decode(chunk2.value), result[1]);
}
else {
throw new Error('The response should take at most 2 chunks.');
}
});
});

View file

@ -0,0 +1,3 @@
---
return Astro.redirect('/destination', 303);
---

View file

@ -0,0 +1,5 @@
import { APIContext } from 'astro';
export async function GET({ redirect }: APIContext) {
return redirect('/destination');
}

View file

@ -0,0 +1,5 @@
import { APIContext } from 'astro';
export async function GET({ url: requestUrl }: APIContext) {
return Response.redirect(new URL('/destination', requestUrl), 307);
}

View file

@ -82,8 +82,7 @@ describe('Prerendering', () => {
});
const html = await res.text();
const $ = cheerio.load(html);
assert.equal(res.status, 301);
assert.equal(res.headers.get('location'), '/some-base/two/');
assert.equal(res.status, 200);
assert.equal($('h1').text(), 'Two');
});
});
@ -171,8 +170,8 @@ describe('Prerendering', () => {
const html = await res.text();
const $ = cheerio.load(html);
expect(res.status).to.equal(200);
expect($('h1').text()).to.equal('One');
assert.equal(res.status, 200);
assert.equal($('h1').text(), 'One');
});
it('Can render prerendered route', async () => {
@ -180,8 +179,8 @@ describe('Prerendering', () => {
const html = await res.text();
const $ = cheerio.load(html);
expect(res.status).to.equal(200);
expect($('h1').text()).to.equal('Two');
assert.equal(res.status, 200);
assert.equal($('h1').text(), 'Two');
});
});
});
@ -252,8 +251,9 @@ describe('Hybrid rendering', () => {
const res = await fetch(`http://${server.host}:${server.port}/some-base/one`, {
redirect: 'manual',
});
assert.equal(res.status, 301);
assert.equal(res.headers.get('location'), '/some-base/one/');
const html = await res.text();
const $ = cheerio.load(html);
assert.equal(res.status, 200);
assert.equal($('h1').text(), 'One');
});
});
@ -342,8 +342,8 @@ describe('Hybrid rendering', () => {
const html = await res.text();
const $ = cheerio.load(html);
expect(res.status).to.equal(200);
expect($('h1').text()).to.equal('shared');
assert.equal(res.status, 200);
assert.equal($('h1').text(), 'shared');
});
});
});