turbo-editors/turbo-corepublic Fork 0
v1.0.2
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

🛟 Updated. 28d5985 · on v1.0.2 · k33g · 15h ago
dockerfile_test.go · 219 lines · 6.7 KBGo Blame HistoryRaw
  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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package syntax

import (
	"strings"
	"testing"
)

// dockerClassOf returns the class covering the first occurrence of a piece of
// text in a Dockerfile.
func dockerClassOf(t *testing.T, src, want string) Class {
	t.Helper()

	index := strings.Index(src, want)
	if index < 0 {
		t.Fatalf("%q does not appear in the source", want)
	}
	line := strings.Count(src[:index], "\n")
	col := index - (strings.LastIndex(src[:index], "\n") + 1)

	class, ok := classAt(Highlight(LanguageDockerfile, src), line, col)
	if !ok {
		t.Fatalf("no span covers %q at line %d column %d", want, line, col)
	}
	return class
}

func TestADockerfileIsRecognisedByItsName(t *testing.T) {
	// It has no extension and no shebang, which is the whole reason Filenames
	// exists.
	for _, name := range []string{
		"Dockerfile", "dockerfile", "Containerfile",
		"Dockerfile.dev", "Dockerfile.prod",
		"docker/Dockerfile", "web.dockerfile",
	} {
		t.Run(name, func(t *testing.T) {
			if got := LanguageOf(name, ""); got != LanguageDockerfile {
				t.Errorf("LanguageOf(%q) = %q, want dockerfile", name, got)
			}
		})
	}
}

func TestAnUnregisteredNameIsStillPlainText(t *testing.T) {
	// The stem rule must not become a wildcard: a Makefile is not a Dockerfile
	// merely because nothing else claims it.
	for _, name := range []string{"Makefile", "Justfile", "LICENSE", "dockerfiles.txt"} {
		t.Run(name, func(t *testing.T) {
			if got := LanguageOf(name, ""); got == LanguageDockerfile {
				t.Errorf("LanguageOf(%q) = dockerfile", name)
			}
		})
	}
}

func TestADotfileHasNoStemToMatchOn(t *testing.T) {
	// ".gitignore" is all extension: its stem is empty, and an empty stem must
	// match nothing rather than everything.
	if got := LanguageOf(".gitignore", ""); got != LanguageNone {
		t.Errorf("LanguageOf(\".gitignore\") = %q, want none", got)
	}
}

func TestAnExtensionOutranksAName(t *testing.T) {
	// A file called Dockerfile.md is documentation about a Dockerfile.
	if got := LanguageOf("Dockerfile.md", ""); got != LanguageMarkdown {
		t.Errorf("LanguageOf(\"Dockerfile.md\") = %q, want markdown", got)
	}
}

func TestDockerfileColoursItsParts(t *testing.T) {
	const src = `# syntax=docker/dockerfile:1
FROM golang:1.26 AS builder
WORKDIR /src
COPY --from=cache /go/pkg /go/pkg
ARG VERSION=1.0
RUN go build -o app .
EXPOSE 8080
CMD ["./app"]
`

	tests := map[string]Class{
		"# syntax=docker/dockerfile:1": ClassComment,
		"FROM":                         ClassKeyword,
		"AS":                           ClassKeyword,
		"WORKDIR":                      ClassKeyword,
		"--from":                       ClassAttribute,
		"8080":                         ClassNumber,
		`"./app"`:                      ClassString,
		"golang:1.26":                  ClassIdentifier,
	}

	for text, want := range tests {
		t.Run(text, func(t *testing.T) {
			if got := dockerClassOf(t, src, text); got != want {
				t.Errorf("%q is %v, want %v", text, got, want)
			}
		})
	}
}

func TestAnInstructionIsMatchedWhateverItsCase(t *testing.T) {
	// Docker accepts any case and real files are inconsistent about it.
	for _, spelling := range []string{"FROM", "from", "From"} {
		t.Run(spelling, func(t *testing.T) {
			if got := dockerClassOf(t, spelling+" alpine\n", spelling); got != ClassKeyword {
				t.Errorf("%q is %v, want keyword", spelling, got)
			}
		})
	}
}

func TestAWordThatIsNotAnInstructionIsAnArgument(t *testing.T) {
	// A continuation line opens with an ordinary word, and colouring it as an
	// instruction would put a keyword in the middle of a shell command.
	const src = "RUN apt-get update \\\n    && apt-get install -y curl\n"

	if got := dockerClassOf(t, src, "apt-get"); got != ClassIdentifier {
		t.Errorf("apt-get is %v, want identifier", got)
	}
}

func TestTheLineContinuationIsColouredAsStructure(t *testing.T) {
	const src = "RUN a \\\n    b\n"
	spans := Highlight(LanguageDockerfile, src)

	class, ok := classAt(spans, 0, len("RUN a "))
	if !ok || class != ClassOperator {
		t.Errorf("the trailing backslash is %v (covered: %v), want operator", class, ok)
	}
}

func TestDockerfileColoursVariables(t *testing.T) {
	for _, variable := range []string{"$HOME", "${VERSION}", "${TAG:-latest}"} {
		t.Run(variable, func(t *testing.T) {
			src := "RUN echo " + variable + "\n"
			if got := dockerClassOf(t, src, variable); got != ClassBuiltin {
				t.Errorf("%q is %v, want builtin", variable, got)
			}
		})
	}
}

func TestAVariableIsOneSpanToItsClosingBrace(t *testing.T) {
	const variable = "${TAG:-latest}"
	src := "RUN echo " + variable + " done\n"
	spans := Highlight(LanguageDockerfile, src)
	start := strings.Index(src, variable)

	for col := start; col < start+len(variable); col++ {
		class, ok := classAt(spans, 0, col)
		if !ok || class != ClassBuiltin {
			t.Fatalf("column %d of %q is %v (covered: %v); the variable was split", col-start, variable, class, ok)
		}
	}
}

func TestAPathOrAnImageReferenceIsOneWord(t *testing.T) {
	// Splitting /usr/local/bin or golang:1.26-alpine would be three colours for
	// one argument, which is noise rather than information. The image reference
	// is the case that matters: it is on the first line of nearly every file.
	tests := map[string]string{
		"WORKDIR /usr/local/bin":  "/usr/local/bin",
		"FROM golang:1.26-alpine": "golang:1.26-alpine",
		"COPY app /opt/app":       "/opt/app",
	}

	for src, word := range tests {
		t.Run(word, func(t *testing.T) {
			spans := Highlight(LanguageDockerfile, src)
			start := strings.Index(src, word)

			for col := start; col < start+len(word); col++ {
				class, ok := classAt(spans, 0, col)
				if !ok || class != ClassIdentifier {
					t.Fatalf("column %d of %q is %v (covered: %v); the word was split", col-start, word, class, ok)
				}
			}
		})
	}
}

func TestDockerfileReturnsOneEntryPerLine(t *testing.T) {
	tests := map[string]int{
		"":                1,
		"FROM alpine":     1,
		"FROM alpine\n":   2,
		"FROM a\nRUN b\n": 3,
	}

	for src, want := range tests {
		if got := len(Highlight(LanguageDockerfile, src)); got != want {
			t.Errorf("Highlight(%q) returned %d lines, want %d", src, got, want)
		}
	}
}

func TestBrokenDockerfileIsStillColoured(t *testing.T) {
	for _, src := range []string{"RUN echo \"unterminated", "FROM", "${", "--", "$"} {
		t.Run(src, func(t *testing.T) {
			if got := len(Highlight(LanguageDockerfile, src)); got != 1 {
				t.Errorf("Highlight(%q) returned %d lines, want 1", src, got)
			}
		})
	}
}

func TestDockerfileSpansAreOrderedAndDoNotOverlap(t *testing.T) {
	const src = "FROM golang:1.26 AS build\nCOPY --chown=me:me . /src\nRUN echo \"${TAG:-x}\" # note\n"

	for line, onLine := range Highlight(LanguageDockerfile, src) {
		previousEnd := 0
		for _, span := range onLine {
			if span.Start < previousEnd {
				t.Errorf("line %d: span %+v starts before the previous one ended at %d", line, span, previousEnd)
			}
			previousEnd = span.End
		}
	}
}