Skip to content

Commit d904df2

Browse files
committed
init
1 parent d1fd133 commit d904df2

13 files changed

+2835
-16
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
}
4747
},
4848
"dependencies": {
49+
"@cloudflare/vitest-pool-workers": "^0.6.8",
4950
"wrangler": "^3.57.1"
5051
},
5152
"packageManager": "[email protected]+sha256.7f63001edc077f1cff96cacba901f350796287a2800dfa83fe898f94183e4f5f"

packages/backend/e2e.test.ts

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
import type { Response } from '@cloudflare/workers-types'
2+
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
3+
import { unstable_dev, UnstableDevWorker } from 'wrangler'
4+
import ezSpawn from "@jsdevtools/ez-spawn"
5+
import pushWorkflowRunInProgressFixture from './fixtures/workflow_run.in_progress.json'
6+
import prWorkflowRunRequestedFixture from './fixtures/pr.workflow_run.requested.json'
7+
import prPullRequestSynchronizeFixture from './fixtures/pr.pull_request.json'
8+
import { setupServer } from 'msw/node'
9+
import { http, HttpResponse } from 'msw'
10+
11+
const server = setupServer(
12+
// Mock the GitHub API installation endpoint
13+
http.get('https://api.github.com/repos/:owner/:repo/installation', (ctx) => {
14+
return HttpResponse.json({
15+
id: 1234567,
16+
access_tokens_url: 'https://api.github.com/app/installations/1234567/access_tokens',
17+
})
18+
}),
19+
20+
// Mock the GitHub API token endpoint
21+
http.post('https://api.github.com/app/installations/:installation_id/access_tokens', (ctx) => {
22+
return HttpResponse.json({
23+
token: 'ghs_mock_token',
24+
expires_at: '2024-03-22T00:00:00Z',
25+
})
26+
})
27+
)
28+
29+
let worker: UnstableDevWorker
30+
31+
beforeAll(async () => {
32+
// Start MSW server
33+
server.listen()
34+
35+
await ezSpawn.async('pnpm cross-env TEST=true pnpm --filter=backend run build', [], {
36+
stdio: "inherit",
37+
shell: true,
38+
});
39+
worker = await unstable_dev('dist/_worker.js', {
40+
config: './wrangler.toml',
41+
})
42+
const url = `${worker.proxyData.userWorkerUrl.protocol}//${worker.proxyData.userWorkerUrl.hostname}:${worker.proxyData.userWorkerUrl.port}`
43+
console.log(url)
44+
await ezSpawn.async(`pnpm cross-env TEST=true API_URL=${url} pnpm --filter=pkg-pr-new run build`, [], {
45+
stdio: "inherit",
46+
shell: true,
47+
});
48+
})
49+
50+
afterAll(() => {
51+
server.close()
52+
})
53+
54+
describe.sequential.each([
55+
[pushWorkflowRunInProgressFixture],
56+
[prWorkflowRunRequestedFixture, prPullRequestSynchronizeFixture]
57+
] as const)('webhook endpoints', (...fixture) => {
58+
const [{event, payload}, pr] = fixture
59+
const mode = pr ? 'pr' : 'commit'
60+
it(`handles ${mode} events`, async () => {
61+
// Send PR event if exists
62+
if (pr) {
63+
const prResponse = await worker.fetch('/webhook', {
64+
method: 'POST',
65+
headers: {
66+
'x-github-delivery': 'd81876a0-e8c4-11ee-8fca-9d3a2baa9707',
67+
'x-github-event': 'pull_request',
68+
},
69+
body: JSON.stringify(pr.payload),
70+
})
71+
expect(await prResponse.json()).toEqual({ ok: true })
72+
}
73+
74+
// Send workflow run event
75+
const response = await worker.fetch('/webhook', {
76+
method: 'POST',
77+
headers: {
78+
'x-github-delivery': 'd81876a0-e8c4-11ee-8fca-9d3a2baa9707',
79+
'x-github-event': event,
80+
},
81+
body: JSON.stringify(payload),
82+
})
83+
expect(await response.json()).toEqual({ ok: true })
84+
})
85+
86+
it(`publishes playgrounds for ${mode}`, async () => {
87+
const env = Object.entries({
88+
TEST: true,
89+
GITHUB_SERVER_URL: new URL(payload.workflow_run.html_url).origin,
90+
GITHUB_REPOSITORY: payload.repository.full_name,
91+
GITHUB_RUN_ID: payload.workflow_run.id,
92+
GITHUB_RUN_ATTEMPT: payload.workflow_run.run_attempt,
93+
GITHUB_ACTOR_ID: payload.sender.id,
94+
GITHUB_SHA: payload.workflow_run.head_sha,
95+
GITHUB_ACTION: payload.workflow_run.id,
96+
GITHUB_JOB: payload.workflow_run.name,
97+
GITHUB_REF_NAME: pr
98+
? `${pr.payload.number}/merge`
99+
: payload.workflow_run.head_branch,
100+
})
101+
.map(([k, v]) => `${k}=${JSON.stringify(v)}`)
102+
.join(" ");
103+
104+
try {
105+
const process = await ezSpawn.async(`pnpm cross-env ${env} pnpm run -w publish:playgrounds`, [], {
106+
stdio: "overlapped",
107+
shell: true,
108+
});
109+
console.log(process.stdout)
110+
111+
} catch (e) {
112+
console.log(e)
113+
114+
}
115+
116+
})
117+
118+
it(`serves and installs playground-a for ${mode}`, async () => {
119+
const [owner, repo] = payload.repository.full_name.split('/')
120+
const sha = payload.workflow_run.head_sha.substring(0, 7)
121+
const ref = pr?.payload.number ?? payload.workflow_run.head_branch
122+
123+
// Test download with SHA
124+
const shaResponse = await worker.fetch(`/${owner}/${repo}/playground-a@${sha}`)
125+
expect(shaResponse.status).toBe(200)
126+
const shaBlob = await shaResponse.blob()
127+
expect(shaBlob.size).toBeGreaterThan(0)
128+
129+
// Test download with ref matches SHA content
130+
const refResponse = await fetchWithRedirect(`/${owner}/${repo}/playground-a@${ref}`)
131+
const refBlob = await refResponse.blob()
132+
const shaBlobSize = await shaBlob.arrayBuffer();
133+
const refBlobSize = await refBlob.arrayBuffer();
134+
expect(shaBlobSize.byteLength).toEqual(refBlobSize.byteLength);
135+
expect(shaBlobSize).toEqual(refBlobSize);
136+
137+
// Test installation
138+
const url = `/${owner}/${repo}/playground-a@${sha}?id=${Date.now()}`
139+
const installProcess = await ezSpawn.async(`pnpm cross-env CI=true npx -f playground-a@${url}`, {
140+
stdio: "overlapped",
141+
shell: true,
142+
});
143+
expect(installProcess.stdout).toContain('playground-a installed successfully!')
144+
})
145+
146+
it(`serves and installs playground-b for ${mode}`, async () => {
147+
const [owner, repo] = payload.repository.full_name.split('/')
148+
const sha = payload.workflow_run.head_sha.substring(0, 7)
149+
150+
// Test download
151+
const response = await worker.fetch(`/${owner}/${repo}/playground-b@${sha}`)
152+
expect(response.status).toBe(200)
153+
154+
// Test installation
155+
const url = `/${owner}/${repo}/playground-b@${sha}?id=${Date.now()}`
156+
const installProcess = await ezSpawn.async(`pnpm cross-env CI=true npx -f playground-b@${url}`, {
157+
stdio: "overlapped",
158+
shell: true,
159+
});
160+
expect(installProcess.stdout).toContain('playground-a installed successfully!') // Should import playground-a
161+
expect(installProcess.stdout).toContain('playground-b installed successfully!')
162+
})
163+
})
164+
165+
describe('URL redirects', () => {
166+
describe('standard packages', () => {
167+
it('redirects full URLs correctly', async () => {
168+
const response = await fetchWithRedirect('/tinylibs/tinybench@a832a55')
169+
expect(response.url).toContain('/tinylibs/tinybench/tinybench@a832a55')
170+
})
171+
172+
it('redirects compact URLs correctly', async () => {
173+
const response = await fetchWithRedirect('/tinybench@a832a55')
174+
expect(response.url).toContain('/tinylibs/tinybench/tinybench@a832a55')
175+
})
176+
})
177+
178+
describe('scoped packages', () => {
179+
const expectedPath = `/stackblitz/sdk/${encodeURIComponent('@stackblitz/sdk')}@a832a55`
180+
181+
it('redirects full scoped package URLs correctly', async () => {
182+
const response = await fetchWithRedirect('/stackblitz/sdk/@stackblitz/sdk@a832a55')
183+
expect(response.url).toContain(expectedPath)
184+
})
185+
186+
it('redirects compact scoped package URLs correctly', async () => {
187+
const response = await fetchWithRedirect('/@stackblitz/sdk@a832a55')
188+
expect(response.url).toContain(expectedPath)
189+
})
190+
})
191+
})
192+
193+
async function fetchWithRedirect(url: string, maxRedirects = 999): Promise<Response> {
194+
const response = await worker.fetch(url, { redirect: 'manual' })
195+
196+
if (response.status >= 300 && response.status < 400 && maxRedirects > 0) {
197+
const location = response.headers.get('location')
198+
if (location) {
199+
return fetchWithRedirect(location, maxRedirects - 1)
200+
}
201+
}
202+
203+
return response
204+
}

0 commit comments

Comments
 (0)