Skip to content
quarry Artifact [727a1d64d9]
Login

Artifact [727a1d64d9]

Artifact 727a1d64d902d26bc9337afcea14a85978542afabf3c4898653027bd122e8639:


package api

import (
	"context"
	"testing"

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

// A rejected submission reaches its author through the listing, with
// the moderator's reason and the text they wrote.
//
// The half of G-118 that survived the first fix. Approval announces
// itself, because the post appears. Rejection announced nothing: the
// row left the listing, the post had never been in the DAG, and the
// reason went to a public modlog the author was never pointed at — so a
// stranger reading that log knew why, and the author did not know it
// had been decided.
//
// The body rides along because the text existed nowhere its author
// could reach. Losing an argument with a moderator is one thing; losing
// the paragraphs is another.
func TestAuthorSeesTheirRejectionAndGetsTheirTextBack(t *testing.T) {
	forum := &heldForumStub{staged: []oltp.Staged{
		{ID: 1, Kind: "post", Title: "eve's refused post", Author: "eve",
			At: "2026-09-19T10:00:00", State: "rejected",
			Reason: "off topic", Body: "the paragraphs eve wrote"},
		{ID: 2, Kind: "post", Title: "eve's waiting post", Author: "eve",
			At: "2026-09-19T11:00:00", State: "pending", Body: "still waiting"},
	}}
	r := ops.NewRegistry()
	if err := RegisterAll(r, Deps{Version: "v", Repos: testRepos(forumTechIndex(t)), Forum: forum}); err != nil {
		t.Fatal(err)
	}
	got, err := r.Invoke(ops.WithUser(context.Background(), "eve"), "forum.list", forumListIn{Repo: "test"})
	if err != nil {
		t.Fatal(err)
	}
	held := got.(forumListOut).Held
	if len(held) != 2 {
		t.Fatalf("FAIL: eve sees %d of her own submissions, want 2: %+v", len(held), held)
	}
	var rejected, pending *forumHeldOut
	for i := range held {
		switch held[i].State {
		case "rejected":
			rejected = &held[i]
		case "pending":
			pending = &held[i]
		}
	}
	if rejected == nil {
		t.Fatalf("FAIL: the rejected submission never reached its author: %+v", held)
	}
	if rejected.Reason != "off topic" {
		t.Errorf("FAIL: the moderator's reason did not reach the author: %q", rejected.Reason)
	}
	if rejected.Body != "the paragraphs eve wrote" {
		t.Errorf("FAIL: the author's text was not handed back: %q", rejected.Body)
	}

	// A PENDING row carries neither: there is no decision to explain,
	// and the author has their own draft. Sending the body back on
	// every listing would be a payload nobody asked for.
	if pending == nil {
		t.Fatalf("FAIL: the waiting submission vanished: %+v", held)
	}
	if pending.Reason != "" || pending.Body != "" {
		t.Errorf("FAIL: a waiting submission carries a reason or a body: %+v", pending)
	}

	// Still nobody else's, rejections included.
	other, err := r.Invoke(ops.WithUser(context.Background(), "jan"), "forum.list", forumListIn{Repo: "test"})
	if err != nil {
		t.Fatal(err)
	}
	if h := other.(forumListOut).Held; len(h) != 0 {
		t.Errorf("FAIL: jan was shown eve's rejection: %+v", h)
	}
}