package api
import (
"errors"
"path/filepath"
"strconv"
"strings"
"testing"
"craftdesign.group/quarry/internal/attach"
"craftdesign.group/quarry/internal/oltp"
"craftdesign.group/quarry/internal/ops"
)
// A chat line carries the files pasted into it: begun with family chat
// under the chat letter, checked before the line is posted, named in
// its text as attach:NAME and resolved against that line alone, and
// gone with the line when it is deleted.
func TestAChatLineCarriesItsFiles(t *testing.T) {
ix, z, _ := attachStore(t)
srv, objects := newObjectS3(t)
buckets := newMemBuckets()
buckets.rows["test"] = oltp.BucketBinding{Endpoint: srv.URL, Bucket: "files", Region: "auto", AccessKey: "AK", Secret: "SK",
LimitBytes: 1 << 20, DeleteObjects: true}
db, err := oltp.Open(filepath.Join(t.TempDir(), "o.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { db.Close() })
d := Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"test": {Index: ix, Zit: z}}),
Chat: &fakeChat{}, Buckets: buckets, Attachments: newMemAttachments(buckets), ChatFiles: db}
r := ops.NewRegistry()
if err := RegisterAll(r, d); err != nil {
t.Fatal(err)
}
begin := func(who, letters string) (attachmentBeginOut, error) {
out, err := r.Invoke(as(who, letters), "attachment.begin", attachmentBeginIn{Repo: "test", Family: "chat",
Name: "shot.png", Size: 500, Type: "image/png", SHA256: sha})
if err != nil {
return attachmentBeginOut{}, err
}
return out.(attachmentBeginOut), nil
}
post := func(who, text string, tickets ...string) (uint64, error) {
out, err := r.Invoke(as(who, "C"), "chat.post", chatPostIn{Repo: "test", Text: text, Attach: tickets})
if err != nil {
return 0, err
}
return out.(chatPostOut).Seq, nil
}
if _, err := begin("lou", "bB"); !errors.Is(err, ops.ErrForbidden) {
t.Errorf("FAIL: a chat upload began without the chat letter: %v", err)
}
b, err := begin("lou", "C")
if err != nil {
t.Fatalf("FAIL: begin for chat: %v", err)
}
if b.Name != "shot.png" {
t.Errorf("FAIL: begin named the file %q", b.Name)
}
line := "look "
if _, err := post("lou", line, b.Ticket); !errors.Is(err, ops.ErrInvalid) {
t.Errorf("FAIL: a line was posted with a file that never reached the bucket: %v", err)
}
objects.objects[attach.Key(sha)] = 500
if _, err := post("ann", line, b.Ticket); !errors.Is(err, ops.ErrForbidden) {
t.Errorf("FAIL: someone else's upload rode on a line: %v", err)
}
if n := len(d.Chat.(*fakeChat).msgs); n != 0 {
t.Fatalf("FAIL: %d line(s) posted by refused sends", n)
}
seq, err := post("lou", line, b.Ticket)
if err != nil {
t.Fatalf("FAIL: post with its file: %v", err)
}
if _, ok, _ := d.Attachments.UploadTicket(b.Ticket); ok {
t.Error("FAIL: the ticket was not spent")
}
got, err := r.Invoke(as("ann", "C"), "chat.get", chatGetIn{Repo: "test", Seq: seq})
if err != nil {
t.Fatal(err)
}
html := got.(chatGetOut).Message.HTML
if want := `/r/test/chat/` + strconv.FormatUint(seq, 10) + `/file/shot.png`; !strings.Contains(html, want) {
t.Errorf("FAIL: the picture does not point at the line's file %s: %s", want, html)
}
// Another line naming the same file holds none of its own.
other, err := post("ann", "")
if err != nil {
t.Fatal(err)
}
if _, err := r.Invoke(as("ann", "C"), "chat.file", chatFileIn{Repo: "test", Seq: other, Name: "shot.png"}); !errors.Is(err, ops.ErrNotFound) {
t.Errorf("FAIL: a line answered for a file it does not carry: %v", err)
}
link, err := r.Invoke(as("ann", "C"), "chat.file", chatFileIn{Repo: "test", Seq: seq, Name: "shot.png"})
if err != nil {
t.Fatalf("FAIL: chat.file: %v", err)
}
if l := link.(chatFileOut); l.Download == "" || l.Inline == "" || l.Type != "image/png" {
t.Errorf("FAIL: chat.file = %+v", l)
}
if _, err := r.Invoke(as("lou", "C"), "chat.delete", chatDeleteIn{Repo: "test", Seq: seq}); err != nil {
t.Fatal(err)
}
if _, err := r.Invoke(as("ann", "C"), "chat.file", chatFileIn{Repo: "test", Seq: seq, Name: "shot.png"}); !errors.Is(err, ops.ErrNotFound) {
t.Errorf("FAIL: a deleted line's file still answers: %v", err)
}
if len(objects.deleted) != 1 || objects.deleted[0] != attach.Key(sha) {
t.Errorf("FAIL: deleting the line deleted %v from the bucket", objects.deleted)
}
t.Log("ok: a chat line's files are checked, resolved against it, and go with it")
}