rastrillo / fiesta Public

Clone
git clone https://amadan.net/rastrillo/fiesta

Plain git — no account needed to clone.

Download

Download this file

1package nodetest
2
3import (
4 "fmt"
5 "os"
6 "runtime"
7 "strings"
8 "testing"
9 "time"
10)
11
12// Run round-trips stdin, Args, Env and Dir through a real node.
13func TestRunRoundTrips(t *testing.T) {
14 out := Run(t, Cmd{
15 Dir: "testdata",
16 Args: []string{"echo.mjs", "a", "b"},
17 Stdin: []byte(`{"hello":"node"}`),
18 Env: []string{"TZ=UTC"},
19 })
20 if got, want := string(out), `{"HELLO":"NODE"} tz=UTC args=a,b`; got != want {
21 t.Fatalf("output = %q, want %q", got, want)
22 }
23}
24
25// recorder captures a Fatal or Skip instead of acting on it, so a test
26// can assert on how Run failed its caller.
27type recorder struct {
28 testing.TB
29 fatal, skip string
30}
31
32func (r *recorder) Helper() {}
33func (r *recorder) Fatalf(format string, args ...any) {
34 r.fatal = fmt.Sprintf(format, args...)
35 runtime.Goexit()
36}
37func (r *recorder) Skipf(format string, args ...any) {
38 r.skip = fmt.Sprintf(format, args...)
39 runtime.Goexit()
40}
41
42func record(t *testing.T, fn func(testing.TB)) *recorder {
43 t.Helper()
44 r := &recorder{TB: t}
45 done := make(chan struct{})
46 go func() {
47 defer close(done)
48 fn(r)
49 }()
50 <-done
51 return r
52}
53
54// A failure reports both streams: node --test explains itself on
55// stdout, a crash on stderr, and dropping either (Tito's first version
56// kept only stderr) leaves the reader with half the story.
57func TestRunFailureCarriesBothStreams(t *testing.T) {
58 Node(t)
59 r := record(t, func(tb testing.TB) { Run(tb, Cmd{Dir: "testdata", Args: []string{"fail.mjs"}}) })
60 for _, want := range []string{"node fail.mjs", "exit status 3", "stdout-says-why", "stderr-says-why"} {
61 if !strings.Contains(r.fatal, want) {
62 t.Errorf("failure message lacks %q:\n%s", want, r.fatal)
63 }
64 }
65}
66
67// A script that never exits fails its test at the timeout, by name,
68// rather than holding the package until go test's own alarm.
69func TestRunTimesOut(t *testing.T) {
70 Node(t)
71 start := time.Now()
72 r := record(t, func(tb testing.TB) {
73 Run(tb, Cmd{Dir: "testdata", Args: []string{"hang.mjs"}, Timeout: 200 * time.Millisecond})
74 })
75 if !strings.Contains(r.fatal, "timed out after 200ms") {
76 t.Fatalf("a hung script gave %q, want the timeout", r.fatal)
77 }
78 if d := time.Since(start); d > 30*time.Second {
79 t.Fatalf("the timeout took %s to fire", d)
80 }
81}
82
83// No node skips, naming the test — and fails instead under RequireEnv,
84// because a CI image that lost node must not retire every JavaScript
85// check in silence.
86func TestMissingNode(t *testing.T) {
87 t.Setenv("PATH", t.TempDir())
88
89 t.Setenv(RequireEnv, "")
90 os.Unsetenv(RequireEnv)
91 r := record(t, func(tb testing.TB) { Node(tb) })
92 if r.fatal != "" || !strings.Contains(r.skip, t.Name()) {
93 t.Errorf("without node: skip %q, fatal %q; want a skip naming %s", r.skip, r.fatal, t.Name())
94 }
95
96 t.Setenv(RequireEnv, "1")
97 r = record(t, func(tb testing.TB) { Node(tb) })
98 if r.skip != "" || !strings.Contains(r.fatal, RequireEnv) {
99 t.Errorf("without node under %s: skip %q, fatal %q; want a failure", RequireEnv, r.skip, r.fatal)
100 }
101}
102