Skip to content
quarry Artifact [3eb9befb46]
Login

Artifact [3eb9befb46]

Artifact 3eb9befb469b4e1a25b33bc139ac961476fafe68a2c8b930325ca98947c49460:


package api

import (
	"context"
	"errors"
	"io"
	"net/http/httptest"
	"strings"
	"testing"

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

// config.export answers a configuration area as a download in the
// format config.import reads (4b8508f3): back up before you replace. A
// repository with no store says so rather than answering empty.
func TestAConfigurationAreaDownloadsForBackup(t *testing.T) {
	dir, _, _, _ := landedStore(t)
	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(map[string]*Repo{
		"alpha": {Zit: zitcli.New(dir)},
		"bare":  {},
	})}); err != nil {
		t.Fatal(err)
	}
	ctx := context.Background()
	out, err := r.Invoke(ctx, "config.export", configExportIn{Repo: "alpha", Area: "user"})
	if err != nil {
		t.Fatalf("FAIL: export: %v", err)
	}
	f, ok := out.(ops.File)
	if !ok {
		t.Fatalf("FAIL: answered %T, not a file", out)
	}
	rc, n, err := f.Open()
	if err != nil {
		t.Fatal(err)
	}
	body, _ := io.ReadAll(rc)
	rc.Close()
	if f.Name() != "alpha-user.txt" || !strings.HasPrefix(f.ContentType(), "text/plain") || f.Immutable() || f.ETag() != "" ||
		int64(len(body)) != n || !strings.HasPrefix(string(body), "#") {
		t.Fatalf("FAIL: %q %q immutable=%v etag=%q, %d/%d bytes: %.60q", f.Name(), f.ContentType(), f.Immutable(), f.ETag(), len(body), n, body)
	}
	if _, err := r.Invoke(ctx, "config.import", configImportIn{Repo: "alpha", File: string(body)}); err != nil {
		t.Fatalf("FAIL: the export does not import back: %v", err)
	}
	if _, err := r.Invoke(ctx, "config.export", configExportIn{Repo: "bare", Area: "shun"}); !errors.Is(err, ops.ErrUnavailable) {
		t.Fatalf("FAIL: a repository with no store answered %v, want unavailable", err)
	}
	// Through the router, where the path is bound and the input
	// validated: both areas answer, anything else is refused. Calling
	// the op directly skipped exactly this, and "oneof=user shun"
	// shipped refusing both (4b8508f3).
	h, err := Router(r, "v")
	if err != nil {
		t.Fatal(err)
	}
	for area, want := range map[string]int{"user": 200, "shun": 200, "ticket": 400} {
		rec := httptest.NewRecorder()
		h.ServeHTTP(rec, httptest.NewRequest("GET", "/api/v1/r/alpha/config/export/"+area, nil))
		if rec.Code != want {
			t.Fatalf("FAIL: GET export/%s answered %d, want %d: %s", area, rec.Code, want, rec.Body)
		}
		if want == 200 && !strings.Contains(rec.Header().Get("Content-Disposition"), "alpha-"+area+".txt") {
			t.Fatalf("FAIL: export/%s is not offered as alpha-%s.txt: %q", area, area, rec.Header().Get("Content-Disposition"))
		}
	}
	t.Logf("ok: alpha-user.txt, %d bytes, imports back; user and shun answer through the router, ticket is refused; a storeless repository is unavailable", n)
}