forked from pkgxdev/pkgx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
integration.suite.ts
158 lines (138 loc) · 4.76 KB
/
integration.suite.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import { describe } from "deno/testing/bdd.ts"
import { assert, assertEquals } from "deno/testing/asserts.ts"
import Path from "path"
interface This {
tea: Path
sandbox: Path
TEA_PREFIX: Path
run: (opts: RunOptions) => Promise<number> & Enhancements
}
type RunOptions = ({
args: string[]
} | {
cmd: string[]
}) & {
env?: Record<string, string>
throws?: boolean
sync?: boolean
}
interface Enhancements {
stdout(): Promise<string>
stderr(): Promise<string>
}
const existing_tea_prefix = Deno.env.get("CI") ? undefined : Path.home().join(".tea").isDirectory()
const suite = describe({
name: "integration tests",
async beforeEach(this: This) {
const tmp = new Path(await Deno.makeTempDir({ prefix: "tea-" }))
const cwd = new URL(import.meta.url).path().parent().parent().string
const TEA_PREFIX = existing_tea_prefix ?? tmp.join('opt').mkdir()
const bin = tmp.join('bin').mkpath()
const proc = Deno.run({
cmd: [
"deno",
"compile",
"--quiet",
"--allow-read", // restricting reads would be nice but Deno.symlink requires read permission to ALL
"--allow-write", // restricting writes would be nice but Deno.symlink requires write permission to ALL
"--allow-net",
"--allow-run",
"--allow-env",
"--unstable",
"--output", bin.join("tea").string,
"src/app.ts"
], cwd
})
assert((await proc.status()).success)
proc.close()
this.tea = bin.join("tea")
assert(this.tea.isExecutableFile())
this.TEA_PREFIX = TEA_PREFIX
assert(this.TEA_PREFIX.isDirectory())
this.sandbox = tmp.join("box").mkdir()
const teafile = bin.join('tea')
const { sandbox } = this
this.run = ({env, throws, sync, ...opts}: RunOptions) => {
sync ??= true
env ??= {}
for (const key of ['HOME', 'CI', 'RUNNER_DEBUG', 'GITHUB_ACTIONS']) {
const value = Deno.env.get(key)
if (value) env[key] = value
}
env['PATH'] = `${bin}:/usr/bin:/bin` // these systems are full of junk so we prune PATH
env['TEA_PREFIX'] ??= TEA_PREFIX.string
env['CLICOLOR_FORCE'] = '1'
let stdout: "piped" | undefined
let stderr: "piped" | undefined
// we delay instantiating the proc so we can set `stdout` if the user calls that function
// so the contract is the user must call `stdout` within this event loop iteration
const p = Promise.resolve().then(async () => {
const cmd = "args" in opts
? [...opts.args]
: [...opts.cmd]
//TODO we typically don’t want silent, we just want ERRORS-ONLY
if ("args" in opts) {
if (!existing_tea_prefix && sync) {
cmd.unshift("--sync")
}
cmd.unshift(teafile.string)
} else if (cmd[0] != 'tea') {
// we need to do an initial --sync
const arg = sync ? "-Ss" : "-s"
const proc = Deno.run({ cmd: [teafile.string, arg], cwd: sandbox.string, env, clearEnv: true })
assertEquals((await proc.status()).code, 0)
proc.close()
}
const proc = Deno.run({ cmd, cwd: sandbox.string, stdout, stderr, env, clearEnv: true})
try {
const status = await proc.status()
if ((throws === undefined || throws) && !status.success) {
if (stdout == 'piped') {
console.error(new TextDecoder().decode(await proc.output()))
try {
proc.stdout?.close()
} catch {
// we cannot figure out how to stop this throwing if it is already closed
// nor how to detect that
}
}
if (stderr == 'piped') {
console.error(new TextDecoder().decode(await proc.stderrOutput()))
try {
proc.stderr?.close()
} catch {
// we cannot figure out how to stop this throwing if it is already closed
// nor how to detect that
}
}
throw status
}
if (stdout == 'piped') {
const out = await proc.output()
return new TextDecoder().decode(out)
} else if (stderr == 'piped') {
const out = await proc.stderrOutput()
return new TextDecoder().decode(out)
} else {
return status.code
}
} finally {
proc.close()
}
}) as Promise<number> & Enhancements
p.stdout = () => {
stdout = "piped"
return p as unknown as Promise<string>
}
p.stderr = () => {
stderr = "piped"
return p as unknown as Promise<string>
}
return p
}
},
afterEach() {
// this.TEA_PREFIX.parent().rm({ recursive: true })
},
})
export default suite