Files
mesh-delivery/modules/mesh-delivery-view/cmd/mesh-delivery-cli/main_test.go
T

170 lines
5.9 KiB
Go

package main
import (
"bytes"
"context"
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/asks"
"git.novox.be/novox/mesh-sdk/go/delivery"
"mesh-delivery-view/internal/view"
)
func serve(t *testing.T) string {
t.Helper()
m := view.New()
m.Apply(view.Entry{Bucket: view.AssignmentsBucket, Key: "a.x", Value: []byte(`{"node":"a","module":"x","build":"b1"}`)})
dir, err := os.MkdirTemp(os.Getenv("TMPDIR"), "c")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.RemoveAll(dir) })
l, err := view.ListenSocket(filepath.Join(dir, "cli.sock"))
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
go view.ServeSocket(ctx, l, m)
t.Cleanup(cancel)
return filepath.Join(dir, "cli.sock")
}
func TestPrintsATable(t *testing.T) { // 1134/AC6
var out, errOut bytes.Buffer
if code := run(nil, serve(t), &out, &errOut); code != 0 {
t.Fatalf("exit %d: %s", code, errOut.String())
}
if !strings.Contains(out.String(), "MODULE") || !strings.Contains(out.String(), "no have yet") {
t.Fatalf("table = %q", out.String())
}
}
func TestJSONFlag(t *testing.T) { // 1134/AC6
var out, errOut bytes.Buffer
if code := run([]string{"--json"}, serve(t), &out, &errOut); code != 0 {
t.Fatalf("exit %d: %s", code, errOut.String())
}
var s view.Snapshot
if err := json.Unmarshal(out.Bytes(), &s); err != nil || len(s.Rows) != 1 || s.Rows[0].Module != "x" {
t.Fatalf("json = %q, %v", out.String(), err)
}
}
func TestFailsLoudWhenTheViewIsNotThere(t *testing.T) { // 1134/AC6
var out, errOut bytes.Buffer
if code := run(nil, filepath.Join(os.Getenv("TMPDIR"), "none.sock"), &out, &errOut); code == 0 || errOut.Len() == 0 {
t.Fatalf("exit %d, stderr %q", code, errOut.String())
}
if code := run([]string{"--start"}, "", &out, &errOut); code != 2 {
t.Fatalf("an unknown flag must exit 2, got %d", code)
}
}
func TestPrintsTheCandidateGroupsAndTheFingerprintComparison(t *testing.T) { // 1241/AC6
m := view.New()
const head = "0123456789abcdef0123456789abcdef01234567"
cand, err := delivery.EncodeCandidate(delivery.Candidate{Group: "feat/x", Pulls: []delivery.CandidatePull{{Head: delivery.Head{Repository: "novox/mesh-delivery", Number: 12, Head: head}, Modules: []string{"a"}}},
HeadsHash: mustHash(t, delivery.Head{Repository: "novox/mesh-delivery", Number: 12, Head: head})})
if err != nil {
t.Fatal(err)
}
mod, err := delivery.EncodeModule(delivery.Module{None: "no tree", Repository: "novox/mesh-delivery", Commit: head})
if err != nil {
t.Fatal(err)
}
m.Apply(view.Entry{Bucket: delivery.CandidatesBucket, Key: "feat/x", Value: cand})
m.Apply(view.Entry{Bucket: delivery.ModulesBucket, Key: "a", Value: mod})
dir, err := os.MkdirTemp(os.Getenv("TMPDIR"), "c")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.RemoveAll(dir) })
l, err := view.ListenSocket(filepath.Join(dir, "cli.sock"))
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
go view.ServeSocket(ctx, l, m)
t.Cleanup(cancel)
var out, errOut bytes.Buffer
if code := run(nil, filepath.Join(dir, "cli.sock"), &out, &errOut); code != 0 {
t.Fatalf("exit %d: %s", code, errOut.String())
}
for _, want := range []string{"CANDIDATES", "feat/x", "novox/mesh-delivery#12", head[:12], "none", "no tree"} {
if !strings.Contains(out.String(), want) {
t.Errorf("the command's table lacks %q:\n%s", want, out.String())
}
}
}
func mustHash(t *testing.T, h delivery.Head) string {
t.Helper()
s, err := delivery.HeadsHash([]delivery.Head{h})
if err != nil {
t.Fatal(err)
}
return s
}
// TestPrintsTheOpenAsksInTheTableAndTheJSON: the command shows what waits for the operator's Approve (1345/AC2). The ask is
// the gate's, as mesh-sdk go/delivery EncodeAsk writes it under the key MergeAskKey gives (as internal/view asks_test.go).
func TestPrintsTheOpenAsksInTheTableAndTheJSON(t *testing.T) { // 1345/AC2
m := view.New()
heads := []delivery.Head{{Repository: "novox/mesh-delivery", Number: 12, Head: "0123456789abcdef0123456789abcdef01234567"}}
key, err := delivery.MergeAskKey("feat/x", heads)
if err != nil {
t.Fatal(err)
}
ask := asks.Ask{ID: delivery.AskID(key), Headline: "Merge into main: mesh-delivery#12", Explanation: "Merges one pull request.", Who: asks.Operator,
Expires: time.Now().Add(time.Hour), OnExpiry: "the merge stays held",
Options: []asks.Option{{ID: "approve", Label: "Approve", Does: "merges", Level: asks.Approve, Binds: "sha256:00"}}}
value, err := delivery.EncodeAsk(delivery.Ask{Group: "feat/x", Heads: heads, Ask: ask})
if err != nil {
t.Fatal(err)
}
m.Apply(view.Entry{Bucket: delivery.AsksBucket, Key: key, Value: value})
dir, err := os.MkdirTemp(os.Getenv("TMPDIR"), "c")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.RemoveAll(dir) })
l, err := view.ListenSocket(filepath.Join(dir, "cli.sock"))
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
go view.ServeSocket(ctx, l, m)
t.Cleanup(cancel)
socket := filepath.Join(dir, "cli.sock")
var out, errOut bytes.Buffer
if code := run(nil, socket, &out, &errOut); code != 0 {
t.Fatalf("exit %d: %s", code, errOut.String())
}
for _, want := range []string{"ASKS", "Merge into main: mesh-delivery#12", "Merges one pull request.", "answer on the phone"} {
if !strings.Contains(out.String(), want) {
t.Errorf("the table lacks %q:\n%s", want, out.String())
}
}
out.Reset()
if code := run([]string{"--json"}, socket, &out, &errOut); code != 0 {
t.Fatalf("exit %d: %s", code, errOut.String())
}
var got struct {
Asks []struct {
Key string `json:"key"`
Headline string `json:"headline"`
How string `json:"how"`
} `json:"asks"`
}
if err := json.Unmarshal(out.Bytes(), &got); err != nil || len(got.Asks) != 1 || got.Asks[0].Key != key ||
got.Asks[0].Headline != "Merge into main: mesh-delivery#12" || got.Asks[0].How != "answer on the phone" {
t.Errorf("json = %s (%v)", out.String(), err)
}
}