Skip to content
quarry Artifact [202d5b528e]
Login

Artifact [202d5b528e]

Artifact 202d5b528ee05c6931bed359597a185038c5fc98581b764acc1c12d07cc72a77:


package api

import (
	"errors"
	"strings"
	"testing"

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

// A field value the repository's vocabulary does not list is refused.
//
// ticket.set stored whatever it was handed. Measured on the forge:
// status="Bogus_Not_A_Status" answered 200 and the ticket then read
// Bogus_Not_A_Status — a state no filter offers and no reader expects
// (7d46f782).
//
// The refusal names the words that would have worked, because a caller
// who guessed one value wrong will otherwise guess again. Same
// reasoning as the unknown-JSON-field refusal: "that is wrong" without
// "here is the set" costs a second round trip and often a second wrong
// guess.
func TestAValueOutsideTheVocabularyIsRefused(t *testing.T) {
	vocab := map[string][]string{
		"status":    {"Open", "Closed"},
		"severity":  {"Severe", "Minor"},
		"subsystem": {}, // free text, by default
	}
	err := refuseUnknownVocab(vocab, map[string]string{"status": "Bogus_Not_A_Status"})
	if !errors.Is(err, ops.ErrInvalid) {
		t.Fatalf("err = %v, want ErrInvalid", err)
	}
	for _, want := range []string{"Bogus_Not_A_Status", "Open", "Closed"} {
		if !strings.Contains(err.Error(), want) {
			t.Errorf("the refusal does not mention %q: %v", want, err)
		}
	}
}

// The values the vocabulary DOES list still go through, and a field the
// vocabulary leaves empty is free text rather than a closed set.
//
// This is the half that must not narrow: subsystem has no default
// words, so checking it would refuse every subsystem anyone has ever
// used. A field the vocabulary does not mention at all is likewise none
// of this check's business.
func TestTheVocabularyDoesNotRefuseWhatItAllows(t *testing.T) {
	vocab := map[string][]string{
		"status":    {"Open", "Closed"},
		"subsystem": {},
	}
	for _, fields := range []map[string]string{
		{"status": "Open"},
		{"status": ""},                          // not being set
		{"subsystem": "anything at all"},        // free text
		{"assignee": "someone"},                 // not a vocabulary field
		{"status": "Closed", "subsystem": "ci"}, // both kinds at once
	} {
		if err := refuseUnknownVocab(vocab, fields); err != nil {
			t.Errorf("%v was refused: %v", fields, err)
		}
	}
}

// A vocabulary that lists nothing for a field cannot refuse anything
// for it — including when the whole vocabulary is missing, which is
// what a repository with no store answers.
func TestAnEmptyVocabularyRefusesNothing(t *testing.T) {
	if err := refuseUnknownVocab(nil, map[string]string{"status": "whatever"}); err != nil {
		t.Errorf("an absent vocabulary refused a value: %v", err)
	}
	if err := refuseUnknownVocab(map[string][]string{}, map[string]string{"status": "whatever"}); err != nil {
		t.Errorf("an empty vocabulary refused a value: %v", err)
	}
}

// The defaults themselves must pass their own check, or every write
// against a repository with no .quarry/tickets.json is refused.
func TestTheDefaultVocabularyAcceptsItsOwnWords(t *testing.T) {
	for field, words := range DefaultTicketVocabulary {
		for _, w := range words {
			if err := refuseUnknownVocab(DefaultTicketVocabulary, map[string]string{field: w}); err != nil {
				t.Errorf("the default vocabulary refuses its own %s %q: %v", field, w, err)
			}
		}
	}
}