Skip to content
quarry Artifact [a9c94d72cc]
Login

Artifact [a9c94d72cc]

Artifact a9c94d72cc6ed89cdcf61185ebeeff4ace8cbd4d5f6ed3f602155f9f393e1aab:


package main

import (
	"errors"
	"os"
	"path/filepath"
	"regexp"
	"testing"
	"time"

	"craftdesign.group/quarry/internal/events"
	"craftdesign.group/quarry/internal/lease"
)

var nodeIDShape = regexp.MustCompile(`^[0-9a-f]{12}$`)

func TestNodeIDSurvivesARestart(t *testing.T) {
	dir := t.TempDir()
	first, err := nodeID("", dir)
	if err != nil {
		t.Fatal(err)
	}
	if !nodeIDShape.MatchString(first) {
		t.Fatalf("FAIL: first id %q is not 12 hex", first)
	}
	again, err := nodeID("", dir)
	if err != nil {
		t.Fatal(err)
	}
	if again != first {
		t.Fatalf("FAIL: same data dir gave %q then %q", first, again)
	}
	fi, err := os.Stat(filepath.Join(dir, "node-id"))
	if err != nil {
		t.Fatalf("FAIL: no node-id file: %v", err)
	}
	if fi.Mode().Perm() != 0o600 {
		t.Errorf("FAIL: node-id mode %v, want 0600", fi.Mode().Perm())
	}
	if left, _ := filepath.Glob(filepath.Join(dir, "node-id.*")); len(left) != 0 {
		t.Errorf("FAIL: temp files left beside node-id: %v", left)
	}
}

func TestNodeIDDiffersAcrossDataDirs(t *testing.T) {
	a, err := nodeID("", t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	b, err := nodeID("", t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	if a == b {
		t.Fatalf("FAIL: two data dirs share id %q", a)
	}
}

func TestNodeIDFlagWinsOverTheFile(t *testing.T) {
	dir := t.TempDir()
	stored, err := nodeID("", dir)
	if err != nil {
		t.Fatal(err)
	}
	got, err := nodeID("edge-1", dir)
	if err != nil {
		t.Fatal(err)
	}
	if got != "edge-1" {
		t.Fatalf("FAIL: override gave %q, want edge-1", got)
	}
	if back, _ := nodeID("", dir); back != stored {
		t.Errorf("FAIL: override rewrote the stored id: %q, want %q", back, stored)
	}
}

func TestNodeIDRefusesAnEmptyFile(t *testing.T) {
	dir := t.TempDir()
	if err := os.WriteFile(filepath.Join(dir, "node-id"), []byte("\n"), 0o600); err != nil {
		t.Fatal(err)
	}
	if id, err := nodeID("", dir); err == nil {
		t.Fatalf("FAIL: empty node-id file accepted as %q", id)
	}
}

// Two nodes behind local proxies both listen on the default --addr. The
// lease owner is the node id, so they must still contend rather than
// each accepting the other's hold as its own.
func TestNodesOnOneAddrDoNotShareALease(t *testing.T) {
	ev, err := events.Start(t.TempDir())
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(ev.Close)
	var mgrs []*lease.Manager
	for range 2 {
		id, err := nodeID("", t.TempDir())
		if err != nil {
			t.Fatal(err)
		}
		m, err := lease.New(ev.JS(), id, time.Second)
		if err != nil {
			t.Fatal(err)
		}
		t.Cleanup(m.Close)
		mgrs = append(mgrs, m)
	}
	if err := mgrs[0].Guard("repo1", func(lease.Token) error { return nil }); err != nil {
		t.Fatalf("FAIL: first node refused: %v", err)
	}
	if err := mgrs[1].Guard("repo1", func(lease.Token) error { return nil }); !errors.Is(err, lease.ErrNotHolder) {
		t.Fatalf("FAIL: second node on the same addr got %v, want ErrNotHolder", err)
	}
}