New Sign in

fierj

Public

Tiny personal git forge

← fierj / access_test.go
package main

import (
	"net/http"
	"net/url"
	"strings"
	"testing"
)

// ---------- Access Control ----------

// TestAnonymousCannotCreateRepo verifies /new redirects to login for anon users.
func TestAnonymousCannotCreateRepo(t *testing.T) {
	ts := newTestServer(t)
	adminClient := ts.client()
	ts.setupUser(t, adminClient, "admin", "admin123")

	anonClient := ts.client()

	// Anon tries to access /new — should be redirected to login
	resp := ts.get(t, anonClient, "/new")
	assertStatus(t, resp, http.StatusOK)
	b := body(t, resp)
	if strings.Contains(b, "Create") && !strings.Contains(b, "Sign in") {
		t.Error("anonymous user should be redirected to login from /new")
	}
}

// TestAnonymousCannotAccessSettings verifies settings redirect for anon.
func TestAnonymousCannotAccessSettings(t *testing.T) {
	ts := newTestServer(t)
	adminClient := ts.client()
	ts.setupUser(t, adminClient, "admin", "admin123")
	ts.createRepo(t, adminClient, "pub", "Public repo")

	anonClient := ts.client()

	resp := ts.get(t, anonClient, "/pub/settings")
	assertStatus(t, resp, http.StatusOK)
	b := body(t, resp)
	if !strings.Contains(b, "Sign in") {
		t.Error("anonymous should be redirected to login for settings")
	}
}

// TestAnonymousCanViewPublicThreads verifies threads are visible on public repos.
func TestAnonymousCanViewPublicThreads(t *testing.T) {
	ts := newTestServer(t)
	adminClient := ts.client()
	ts.setupUser(t, adminClient, "admin", "admin123")
	ts.createRepo(t, adminClient, "discuss", "Discussion repo")

	// Create a thread as admin
	resp := ts.postForm(t, adminClient, "/discuss/threads/new", url.Values{
		"title": {"Public discussion"},
		"body":  {"Anyone can read this."},
	})
	assertStatus(t, resp, http.StatusOK)

	// Get thread ID
	rp := repoPath(ts.cfg, "discuss")
	threads, _, _ := listThreadsFiltered(rp, "open")
	if len(threads) != 1 {
		t.Fatalf("expected 1 thread, got %d", len(threads))
	}

	// Anon can view the thread
	anonClient := ts.client()
	resp = ts.get(t, anonClient, "/discuss/threads/"+threads[0].ID)
	assertStatus(t, resp, http.StatusOK)
	b := body(t, resp)
	if !strings.Contains(b, "Public discussion") {
		t.Error("anonymous should see public thread")
	}
}

// TestAnonymousCanViewPublicPatches verifies patches are visible on public repos.
func TestAnonymousCanViewPublicPatches(t *testing.T) {
	ts := newTestServer(t)
	adminClient := ts.client()
	ts.setupUser(t, adminClient, "admin", "admin123")
	ts.createRepo(t, adminClient, "patches", "Patches repo")

	// Push something so we have a branch
	localDir := t.TempDir()
	bareRepo := repoPath(ts.cfg, "patches")
	gitCmd(t, localDir, "clone", "file://"+bareRepo, "repo")
	localRepo := localDir + "/repo"
	writeFile(t, localRepo, "f.txt", "hello")
	gitCmd(t, localRepo, "add", "f.txt")
	gitCmd(t, localRepo, "commit", "-m", "init")
	gitCmd(t, localRepo, "push", "origin", "main")

	gitCmd(t, localRepo, "checkout", "-b", "fix")
	writeFile(t, localRepo, "f.txt", "fixed")
	gitCmd(t, localRepo, "add", "f.txt")
	gitCmd(t, localRepo, "commit", "-m", "fix")
	gitCmd(t, localRepo, "push", "origin", "fix")

	// Create a patch
	resp := ts.postForm(t, adminClient, "/patches/patches/new", url.Values{
		"title":  {"Test patch"},
		"body":   {"Patch body"},
		"branch": {"fix"},
	})
	assertStatus(t, resp, http.StatusOK)

	rp := repoPath(ts.cfg, "patches")
	patches, _ := listPatches(rp, "open")
	if len(patches) != 1 {
		t.Fatalf("expected 1 patch, got %d", len(patches))
	}

	// Anon can view
	anonClient := ts.client()
	resp = ts.get(t, anonClient, "/patches/patches/"+patches[0].ID)
	assertStatus(t, resp, http.StatusOK)
	b := body(t, resp)
	if !strings.Contains(b, "Test patch") {
		t.Error("anonymous should see public patch")
	}
}

// TestLoggedInSeesPrivateRepos verifies private repos appear for authenticated users.
func TestLoggedInSeesPrivateRepos(t *testing.T) {
	ts := newTestServer(t)
	c := ts.client()
	ts.setupUser(t, c, "alice", "pass123")
	ts.createRepo(t, c, "secret", "Private repo")

	// Make it private
	ts.postForm(t, c, "/secret/settings", url.Values{
		"description": {"Private repo"},
		"is_private":  {"true"},
	})

	// Logged-in user sees it on home page
	resp := ts.get(t, c, "/")
	assertStatus(t, resp, http.StatusOK)
	b := body(t, resp)
	if !strings.Contains(b, "secret") {
		t.Error("logged-in user should see private repos on home page")
	}
}

// TestAnonCannotCreateThreadOnPrivateRepo verifies POST requires login on private repos.
func TestAnonCannotCreateThreadOnPrivateRepo(t *testing.T) {
	ts := newTestServer(t)
	adminClient := ts.client()
	ts.setupUser(t, adminClient, "admin", "admin123")
	ts.createRepo(t, adminClient, "priv", "Private")
	// Make it private
	ts.postForm(t, adminClient, "/priv/settings", url.Values{
		"description": {"Private"},
		"is_private":  {"true"},
	})

	anonClient := ts.client()
	resp := ts.postForm(t, anonClient, "/priv/threads/new", url.Values{
		"title": {"Should not work"},
		"body":  {"Anon thread"},
	})
	assertStatus(t, resp, http.StatusOK)
	b := body(t, resp)
	if !strings.Contains(b, "Sign in") {
		t.Error("anonymous should not create threads on private repos")
	}
}

// TestAnonCannotCreatePatchOnPrivateRepo verifies POST requires login on private repos.
func TestAnonCannotCreatePatchOnPrivateRepo(t *testing.T) {
	ts := newTestServer(t)
	adminClient := ts.client()
	ts.setupUser(t, adminClient, "admin", "admin123")
	ts.createRepo(t, adminClient, "priv", "Private")
	// Make it private
	ts.postForm(t, adminClient, "/priv/settings", url.Values{
		"description": {"Private"},
		"is_private":  {"true"},
	})

	anonClient := ts.client()
	resp := ts.postForm(t, anonClient, "/priv/patches/new", url.Values{
		"title": {"Should not work"},
		"body":  {"Anon patch"},
	})
	assertStatus(t, resp, http.StatusOK)
	b := body(t, resp)
	if !strings.Contains(b, "Sign in") {
		t.Error("anonymous should not create patches on private repos")
	}
}