Skip to content

Commit bc60124

Browse files
committed
first tests of validateGitHubLink
1 parent 476ebec commit bc60124

File tree

3 files changed

+102
-26
lines changed

3 files changed

+102
-26
lines changed

src/deploy.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -290,26 +290,29 @@ class Deployer {
290290
// accessible and matches the local repository and branch.
291291
// TODO: validate local/remote refs match, "Your branch is up to date",
292292
// and "nothing to commit, working tree clean".
293-
if (deployTarget.project.source) {
294-
if (localRepo && deployTarget.project.source.provider_id !== localRepo.provider_id) {
293+
const {source} = deployTarget.project;
294+
if (source) {
295+
if (localRepo && source.provider_id !== localRepo.provider_id) {
295296
throw new CliError(
296297
`Configured repository does not match local repository; check build settings on ${link(
297298
`${settingsUrl(deployTarget)}/settings`
298299
)}`
299300
);
300301
}
301-
if (localRepo && deployTarget.project.source.branch !== branch) {
302+
if (localRepo && source.branch && source.branch !== branch) {
303+
// TODO: If source.branch is empty, it'll use the default repository
304+
// branch (usually main or master), which we don't know from our current
305+
// getGitHubRepository response, and thus can't check here.
302306
throw new CliError(
303307
`Configured branch does not match local branch; check build settings on ${link(
304308
`${settingsUrl(deployTarget)}/settings`
305309
)}`
306310
);
307311
}
308312
const remoteAuthedRepo = await this.apiClient.getGitHubRepository({
309-
providerId: deployTarget.project.source.provider_id
313+
providerId: source.provider_id
310314
});
311315
if (!remoteAuthedRepo) {
312-
console.log(deployTarget.project.source.provider_id, remoteAuthedRepo);
313316
throw new CliError(
314317
`Cannot access configured repository; check build settings on ${link(
315318
`${settingsUrl(deployTarget)}/settings`

test/deploy-test.ts

Lines changed: 91 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import assert, {fail} from "node:assert";
2+
import {exec} from "node:child_process";
23
import type {Stats} from "node:fs";
3-
import {stat} from "node:fs/promises";
4+
import {mkdtemp, rm, stat} from "node:fs/promises";
5+
import {tmpdir} from "node:os";
6+
import {join} from "node:path";
47
import {Readable, Writable} from "node:stream";
8+
import {promisify} from "node:util";
59
import type {BuildManifest} from "../src/build.js";
610
import {normalizeConfig, setCurrentDate} from "../src/config.js";
711
import type {DeployEffects, DeployOptions} from "../src/deploy.js";
@@ -115,7 +119,9 @@ class MockDeployEffects extends MockAuthEffects implements DeployEffects {
115119
async getDeployConfig(sourceRoot: string, deployConfigPath?: string): Promise<DeployConfig> {
116120
const key = this.getDeployConfigKey(sourceRoot, deployConfigPath);
117121
return (
118-
this.deployConfigs[key] ?? this.defaultDeployConfig ?? {projectId: null, projectSlug: null, workspaceLogin: null}
122+
this.deployConfigs[key] ??
123+
this.defaultDeployConfig ??
124+
({projectId: null, projectSlug: null, workspaceLogin: null, continuousDeployment: null} satisfies DeployConfig)
119125
);
120126
}
121127

@@ -194,11 +200,89 @@ const DEPLOY_CONFIG: DeployConfig & {projectId: string; projectSlug: string; wor
194200
continuousDeployment: false
195201
};
196202

203+
function mockIsolatedDirectory({git}: {git: boolean}) {
204+
let dir: string;
205+
let cwd: string;
206+
beforeEach(async () => {
207+
cwd = process.cwd();
208+
dir = await mkdtemp(join(tmpdir(), "framework-test-"));
209+
process.chdir(dir);
210+
if (git) (await promisify(exec)("git init")).stdout;
211+
});
212+
213+
afterEach(async () => {
214+
process.chdir(cwd);
215+
await rm(dir, {recursive: true});
216+
});
217+
}
218+
197219
describe("deploy", () => {
198220
before(() => setCurrentDate(new Date("2024-01-10T16:00:00")));
199221
mockObservableApi();
200222
mockJsDelivr();
201223

224+
describe("in isolated directory with git repo", () => {
225+
mockIsolatedDirectory({git: true});
226+
227+
it("fails continuous deployment if repo has no GitHub remote", async () => {
228+
getCurrentObservableApi()
229+
.handleGetCurrentUser()
230+
.handleGetWorkspaceProjects({
231+
workspaceLogin: DEPLOY_CONFIG.workspaceLogin,
232+
projects: []
233+
})
234+
.handlePostProject({projectId: DEPLOY_CONFIG.projectId})
235+
.start();
236+
const effects = new MockDeployEffects();
237+
effects.clack.inputs.push(
238+
true, // No apps found. Do you want to create a new app?
239+
"cloud-deployed-app", // What slug do you want to use?
240+
"public", // Who is allowed to access your app?
241+
true // Do you want to enable continuous deployment?
242+
);
243+
244+
try {
245+
await deploy(TEST_OPTIONS, effects);
246+
assert.fail("expected error");
247+
} catch (error) {
248+
CliError.assert(error, {message: "No GitHub remote found."});
249+
}
250+
251+
effects.close();
252+
});
253+
});
254+
255+
describe("in isolated directory without git repo", () => {
256+
mockIsolatedDirectory({git: false});
257+
258+
it("fails continuous deployment if not in a git repo", async () => {
259+
getCurrentObservableApi()
260+
.handleGetCurrentUser()
261+
.handleGetWorkspaceProjects({
262+
workspaceLogin: DEPLOY_CONFIG.workspaceLogin,
263+
projects: []
264+
})
265+
.handlePostProject({projectId: DEPLOY_CONFIG.projectId})
266+
.start();
267+
const effects = new MockDeployEffects();
268+
effects.clack.inputs.push(
269+
true, // No apps found. Do you want to create a new app?
270+
"cloud-deployed-app", // What slug do you want to use?
271+
"public", // Who is allowed to access your app?
272+
true // Do you want to enable continuous deployment?
273+
);
274+
275+
try {
276+
await deploy(TEST_OPTIONS, effects);
277+
assert.fail("expected error");
278+
} catch (error) {
279+
CliError.assert(error, {message: "Not at root of a git repository."});
280+
}
281+
282+
effects.close();
283+
});
284+
});
285+
202286
it("makes expected API calls for an existing project", async () => {
203287
const deployId = "deploy456";
204288
getCurrentObservableApi()
@@ -334,6 +418,7 @@ describe("deploy", () => {
334418
effects.clack.inputs.push(
335419
DEPLOY_CONFIG.projectSlug, // which project do you want to use?
336420
true, // Do you want to continue? (and overwrite the project)
421+
false, // Do you want to enable continuous deployment?
337422
"change project title" // "what changed?"
338423
);
339424
await deploy(TEST_OPTIONS, effects);
@@ -887,10 +972,7 @@ describe("deploy", () => {
887972
force: null,
888973
config: {...TEST_OPTIONS.config, output: "test/output/does-not-exist"}
889974
} satisfies DeployOptions;
890-
getCurrentObservableApi()
891-
.handleGetCurrentUser()
892-
.handleGetProject(DEPLOY_CONFIG)
893-
.start();
975+
getCurrentObservableApi().handleGetCurrentUser().handleGetProject(DEPLOY_CONFIG).start();
894976
const effects = new MockDeployEffects({
895977
deployConfig: DEPLOY_CONFIG,
896978
fixedInputStatTime: new Date("2024-03-09"),
@@ -905,10 +987,7 @@ describe("deploy", () => {
905987
...TEST_OPTIONS,
906988
force: null
907989
} satisfies DeployOptions;
908-
getCurrentObservableApi()
909-
.handleGetCurrentUser()
910-
.handleGetProject(DEPLOY_CONFIG)
911-
.start();
990+
getCurrentObservableApi().handleGetCurrentUser().handleGetProject(DEPLOY_CONFIG).start();
912991
const effects = new MockDeployEffects({
913992
deployConfig: DEPLOY_CONFIG,
914993
fixedInputStatTime: new Date("2024-03-09"),
@@ -926,10 +1005,7 @@ describe("deploy", () => {
9261005
...TEST_OPTIONS,
9271006
force: null
9281007
} satisfies DeployOptions;
929-
getCurrentObservableApi()
930-
.handleGetCurrentUser()
931-
.handleGetProject(DEPLOY_CONFIG)
932-
.start();
1008+
getCurrentObservableApi().handleGetCurrentUser().handleGetProject(DEPLOY_CONFIG).start();
9331009
const effects = new MockDeployEffects({
9341010
deployConfig: DEPLOY_CONFIG,
9351011
fixedInputStatTime: new Date("2024-03-11"),
@@ -947,10 +1023,7 @@ describe("deploy", () => {
9471023
...TEST_OPTIONS,
9481024
force: "build"
9491025
} satisfies DeployOptions;
950-
getCurrentObservableApi()
951-
.handleGetCurrentUser()
952-
.handleGetProject(DEPLOY_CONFIG)
953-
.start();
1026+
getCurrentObservableApi().handleGetCurrentUser().handleGetProject(DEPLOY_CONFIG).start();
9541027
const effects = new MockDeployEffects({
9551028
deployConfig: DEPLOY_CONFIG,
9561029
fixedInputStatTime: new Date("2024-03-09"),

test/mocks/observableApi.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class ObservableApiMock {
169169
owner: {id: "workspace-id", login: "workspace-login"},
170170
latestCreatedDeployId: null,
171171
automatic_builds_enabled: null,
172-
build_environment_id: null,
172+
build_environment_id: "abc123",
173173
source: null
174174
} satisfies GetProjectResponse)
175175
: emptyErrorBody;
@@ -209,8 +209,8 @@ class ObservableApiMock {
209209
owner,
210210
creator,
211211
latestCreatedDeployId: null,
212-
automatic_builds_enabled: null,
213-
build_environment_id: null,
212+
automatic_builds_enabled: true,
213+
build_environment_id: "abc123",
214214
source: null
215215
} satisfies GetProjectResponse)
216216
: emptyErrorBody;

0 commit comments

Comments
 (0)