Skip to content
quarry Artifact [f5a32ab0db]
Login

Artifact [f5a32ab0db]

Artifact f5a32ab0db0cad9e3496656f95f225def0f1593849da51fb40ce7304f0b3d7ac:


package api

import (
	"errors"
	"strings"
	"testing"

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

// The tracker's vocabulary is the gate. fossil creates a ticket column
// on demand, so an unchecked field map would turn every ticket write
// into a schema migration — and one typo would leave a tracker with an
// "assigne" column forever.
func TestOnlyFieldsTheTrackerDefinesAreWritable(t *testing.T) {
	known := map[string]bool{"assignee": true, "closer": true}
	got, err := extraTicketFields(known, map[string]string{"assignee": "dan"})
	if err != nil {
		t.Fatalf("FAIL: a field the tracker carries was refused: %v", err)
	}
	if got["assignee"] != "dan" {
		t.Errorf("FAIL: assignee = %q, want dan", got["assignee"])
	}
	_, err = extraTicketFields(known, map[string]string{"assigne": "dan"})
	if !errors.Is(err, ops.ErrNotFound) {
		t.Errorf("FAIL: a column the tracker does not define was accepted: %v", err)
	}
}

// A field with its own input is refused here. One value with two doors
// is a disagreement waiting for its first save.
func TestAFieldWithItsOwnInputIsRefusedOnTheGenericDoor(t *testing.T) {
	known := map[string]bool{"status": true, "title": true, "assignee": true}
	for _, name := range []string{"status", "title", "comment", "foundin"} {
		_, err := extraTicketFields(known, map[string]string{name: "x"})
		if !errors.Is(err, ops.ErrInvalid) {
			t.Errorf("FAIL: %q was accepted on the generic door: %v", name, err)
		}
	}
}

// What fossil withholds, quarry does not author. private_contact is
// labelled "Not publicly visible" in fossil's own template; writing it
// through a forge form would put an address into a permanent,
// synchronized record from a page that never showed it.
func TestWithheldFieldsAreNeverAuthored(t *testing.T) {
	known := map[string]bool{"private_contact": true, "contact": true}
	for name := range known {
		_, err := extraTicketFields(known, map[string]string{name: "[email protected]"})
		if !errors.Is(err, ops.ErrInvalid) {
			t.Errorf("FAIL: %q was authorable: %v", name, err)
		}
	}
}

// An empty value CLEARS the field. It is the only way to unset one, and
// a form that could set but never clear would trap a wrong assignee on
// a ticket forever.
func TestAnEmptyValueClearsRatherThanBeingDropped(t *testing.T) {
	got, err := extraTicketFields(map[string]bool{"assignee": true},
		map[string]string{"assignee": ""})
	if err != nil {
		t.Fatal(err)
	}
	v, ok := got["assignee"]
	if !ok || v != "" {
		t.Errorf("FAIL: clearing a field produced %q, present=%v", v, ok)
	}
}

// A name that is not a fossil column shape is refused before it is
// looked up: anything else would have to be quoted in the tracker's own
// SQL, which is a column no tracker has.
func TestOnlyPlainColumnNames(t *testing.T) {
	for _, bad := range []string{"", "1st", "As-signee", "a b", "assignee;drop",
		strings.Repeat("a", 41)} {
		if plainFieldName(bad) {
			t.Errorf("FAIL: %q reads as a column name", bad)
		}
	}
	for _, ok := range []string{"assignee", "found_in", "a1", "closer"} {
		if !plainFieldName(ok) {
			t.Errorf("FAIL: %q does not read as a column name", ok)
		}
	}
}

// One change sets a triage step's worth of fields, not a schema dump.
func TestOneChangeIsBounded(t *testing.T) {
	known, in := map[string]bool{}, map[string]string{}
	for i := 0; i < maxExtraFields+1; i++ {
		n := "f" + string(rune('a'+i%26)) + string(rune('a'+i/26))
		known[n], in[n] = true, "x"
	}
	if _, err := extraTicketFields(known, in); !errors.Is(err, ops.ErrInvalid) {
		t.Errorf("FAIL: an oversized change was accepted: %v", err)
	}
}

// A tracker with no tickets authors nothing through this door, and that
// is the honest answer: there is no schema to read, and inventing
// columns on a fresh tracker is what the check exists to stop.
func TestAnEmptyTrackerAuthorsNoExtraFields(t *testing.T) {
	if _, err := extraTicketFields(nil, map[string]string{"assignee": "dan"}); !errors.Is(err, ops.ErrNotFound) {
		t.Errorf("FAIL: an empty tracker accepted a column: %v", err)
	}
	// And a call with nothing to set is not an error.
	if got, err := extraTicketFields(nil, nil); err != nil || got != nil {
		t.Errorf("FAIL: an empty map = %v, %v", got, err)
	}
}