53007a9257
Compile a host's catalog in a base and a test environment over the puppetserver API, then diff the two. - add catalog-diff CLI with cobra flags, config precedence and exit codes - add puppetserver mTLS client for POST /puppet/v4/catalog - add pdbmux fact client - add catalog normalisation, diff engine and text/JSON renderers - add Makefile, nfpm RPM packaging and woodpecker pipelines
141 lines
4.0 KiB
Go
141 lines
4.0 KiB
Go
package catalog
|
|
|
|
import (
|
|
"bytes"
|
|
"flag"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
var update = flag.Bool("update", false, "rewrite the golden files")
|
|
|
|
func golden(t *testing.T, name string, got []byte) {
|
|
t.Helper()
|
|
path := filepath.Join("testdata", name)
|
|
if *update {
|
|
if err := os.WriteFile(path, got, 0o644); err != nil {
|
|
t.Fatalf("writing golden %s: %v", path, err)
|
|
}
|
|
return
|
|
}
|
|
want, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("reading golden %s: %v (run go test -update)", path, err)
|
|
}
|
|
if !bytes.Equal(want, got) {
|
|
t.Errorf("output does not match %s\n--- want ---\n%s\n--- got ---\n%s", path, want, got)
|
|
}
|
|
}
|
|
|
|
func testReport(t *testing.T) Report {
|
|
t.Helper()
|
|
return NewReport("host.example.net", "develop", "benvin_test", changeSet(t))
|
|
}
|
|
|
|
func TestRenderTextGolden(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
if err := RenderText(&buf, testReport(t), RenderOptions{SourceFileLine: true}); err != nil {
|
|
t.Fatalf("RenderText: %v", err)
|
|
}
|
|
golden(t, "report.txt", buf.Bytes())
|
|
}
|
|
|
|
func TestRenderJSONGolden(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
if err := RenderJSON(&buf, testReport(t)); err != nil {
|
|
t.Fatalf("RenderJSON: %v", err)
|
|
}
|
|
golden(t, "report.json", buf.Bytes())
|
|
}
|
|
|
|
func TestRenderTextNoChanges(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
if err := RenderText(&buf, NewReport("host.example.net", "develop", "benvin_test", nil), RenderOptions{}); err != nil {
|
|
t.Fatalf("RenderText: %v", err)
|
|
}
|
|
if !strings.Contains(buf.String(), "No differences between environments.") {
|
|
t.Errorf("unexpected empty-diff output:\n%s", buf.String())
|
|
}
|
|
}
|
|
|
|
func TestRenderTextColourOnlyWhenAsked(t *testing.T) {
|
|
var plain, coloured bytes.Buffer
|
|
report := testReport(t)
|
|
if err := RenderText(&plain, report, RenderOptions{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := RenderText(&coloured, report, RenderOptions{Colour: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(plain.String(), "\033[") {
|
|
t.Error("plain output contains ANSI escapes")
|
|
}
|
|
if !strings.Contains(coloured.String(), "\033[") {
|
|
t.Error("coloured output contains no ANSI escapes")
|
|
}
|
|
}
|
|
|
|
func TestRenderTextGroupsCriticalFirst(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
if err := RenderText(&buf, testReport(t), RenderOptions{SourceFileLine: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
out := buf.String()
|
|
crit, other := strings.Index(out, "CRITICAL"), strings.Index(out, "OTHER")
|
|
if crit < 0 || other < 0 || crit > other {
|
|
t.Errorf("critical changes not listed first:\n%s", out)
|
|
}
|
|
}
|
|
|
|
func TestRenderJSONEmptyChangesIsArray(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
if err := RenderJSON(&buf, NewReport("host.example.net", "develop", "benvin_test", nil)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(buf.String(), `"changes": []`) {
|
|
t.Errorf("empty change list must render as [], got:\n%s", buf.String())
|
|
}
|
|
}
|
|
|
|
func TestRenderTextCollapsesAddedResources(t *testing.T) {
|
|
report := testReport(t)
|
|
|
|
var collapsed bytes.Buffer
|
|
if err := RenderText(&collapsed, report, RenderOptions{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(collapsed.String(), "message: hi") {
|
|
t.Errorf("added resource expanded without --display-detail-add:\n%s", collapsed.String())
|
|
}
|
|
|
|
var detailed bytes.Buffer
|
|
if err := RenderText(&detailed, report, RenderOptions{DetailAdd: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(detailed.String(), "message: hi") {
|
|
t.Errorf("--display-detail-add did not expand the added resource:\n%s", detailed.String())
|
|
}
|
|
}
|
|
|
|
func TestRenderTextSourceFileLineIsOptional(t *testing.T) {
|
|
report := testReport(t)
|
|
|
|
var plain bytes.Buffer
|
|
if err := RenderText(&plain, report, RenderOptions{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(plain.String(), "site.pp") {
|
|
t.Errorf("file:line printed without --display-source-file-line:\n%s", plain.String())
|
|
}
|
|
|
|
var located bytes.Buffer
|
|
if err := RenderText(&located, report, RenderOptions{SourceFileLine: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(located.String(), "site.pp:19") {
|
|
t.Errorf("--display-source-file-line did not print the source:\n%s", located.String())
|
|
}
|
|
}
|