Skip to content
quarry Artifact [3425cdfc9c]
Login

Artifact [3425cdfc9c]

Artifact 3425cdfc9cecde249e9df4ec153622fa290b4c35fc47e2a8b6307872e7aace91:


package api

import (
	"context"
	"errors"
	"strings"
	"testing"

	"craftdesign.group/quarry/internal/ops"
)

func landingListRegistry(t *testing.T, settled string) *ops.Registry {
	t.Helper()
	r := ops.NewRegistry()
	ix := landingIndex(t, "bob", "feature-x", "trunk", settled)
	if err := RegisterAll(r, Deps{Version: "v", Repos: testRepos(ix)}); err != nil {
		t.Fatal(err)
	}
	return r
}

func landings(t *testing.T, r *ops.Registry, state string) (landingListOut, error) {
	t.Helper()
	got, err := r.Invoke(context.Background(), "landing.list",
		landingListIn{Repo: "test", State: state})
	if err != nil {
		return landingListOut{}, err
	}
	return got.(landingListOut), nil
}

// `state=all` means every state, because that is the value the web
// page's own filter links to.
//
// It used to be passed to the query as a literal state, matching
// nothing, so the op answered 200 with an empty list — the same answer
// a repository with no landings gives. A client written by reading this
// product's UI asked for the one value the op did not accept and was
// told there was nothing there (G-132).
func TestLandingStateAllMeansEveryState(t *testing.T) {
	r := landingListRegistry(t, "accept")
	every, err := landings(t, r, "")
	if err != nil {
		t.Fatal(err)
	}
	all, err := landings(t, r, "all")
	if err != nil {
		t.Fatalf("FAIL: state=all was refused: %v", err)
	}
	if len(every.Landings) == 0 {
		t.Fatal("FAIL: the fixture has no landings to compare")
	}
	if len(all.Landings) != len(every.Landings) {
		t.Fatalf("FAIL: state=all listed %d, no filter listed %d; they must agree",
			len(all.Landings), len(every.Landings))
	}
	t.Logf("ok: state=all and no filter both list %d", len(all.Landings))
}

// A state the op does not know is refused by name, as ticket.state
// already did. Same concept, same parameter name — they used to get
// opposite treatment, which was the sharpest line in the sweep that
// found this.
func TestAnUnknownLandingStateIsRefused(t *testing.T) {
	// ops.Validate is the mechanism, and the router is the only caller
	// of it — the same seam that already refuses an unknown
	// ticket.state. Asserted here rather than over HTTP so the test
	// names what enforces the rule.
	if err := ops.Validate(landingListIn{Repo: "test", State: "zzz-not-a-state"}); !errors.Is(err, ops.ErrInvalid) {
		t.Fatalf("FAIL: err = %v, want ErrInvalid", err)
	} else {
		t.Logf("ok: %v", err)
	}
	// The words that DO work keep working, including the empty one,
	// and they work end to end rather than only passing validation.
	r := landingListRegistry(t, "accept")
	for _, s := range []string{"", "open", "accepted", "rejected", "all"} {
		if err := ops.Validate(landingListIn{Repo: "test", State: s}); err != nil {
			t.Errorf("FAIL: state %q did not validate: %v", s, err)
			continue
		}
		if _, err := landings(t, r, s); err != nil {
			t.Errorf("FAIL: state %q was refused by the op: %v", s, err)
		}
	}
	t.Log("ok: open, accepted, rejected, all and the empty default are accepted")
}

// The refusal names what would have worked. "no such value" alone costs
// the caller a second guess, and the guess is what caused this.
func TestTheLandingStateRefusalNamesTheValues(t *testing.T) {
	err := ops.Validate(landingListIn{Repo: "test", State: "closed"})
	if err == nil {
		t.Fatal("FAIL: a state the op does not know was accepted")
	}
	for _, want := range []string{"open", "accepted", "rejected", "all"} {
		if !strings.Contains(err.Error(), want) {
			t.Errorf("FAIL: the refusal does not mention %q: %v", want, err)
		}
	}
	t.Logf("ok: %v", err)
}