clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

main
tinyrelay / internal / daemon / nip96_test.go
nip96_test.go 3.3K
1package daemon23import (4 "bytes"5 "context"6 "encoding/json"7 "io"8 "mime/multipart"9 "net/http"10 "net/http/httptest"11 "net/textproto"12 "strings"13 "testing"1415 "github.com/FelineStateMachine/tinyrelay/internal/event"16)1718func TestNIP96MultipartUploadGetAndDeleteOnNonDefaultTenant(t *testing.T) {19 ctx := context.Background()20 secret := strings.Repeat("0", 63) + "1"21 owner, err := event.PublicKey(secret)22 if err != nil {23 t.Fatal(err)24 }25 app, err := New(ctx, Config{DataDir: t.TempDir(), DefaultTenant: "main"})26 if err != nil {27 t.Fatal(err)28 }29 defer app.Close(ctx)30 meta, err := app.Create(ctx, CreateOptions{Name: "media", Owner: owner, Template: "default"})31 if err != nil {32 t.Fatal(err)33 }3435 var body bytes.Buffer36 form := multipart.NewWriter(&body)37 part, err := form.CreatePart(textproto.MIMEHeader{38 "Content-Disposition": {`form-data; name="file"; filename="photo.webp"`},39 "Content-Type": {"image/webp"},40 })41 if err != nil {42 t.Fatal(err)43 }44 if _, err := io.WriteString(part, "webp payload"); err != nil {45 t.Fatal(err)46 }47 if err := form.Close(); err != nil {48 t.Fatal(err)49 }50 raw := append([]byte(nil), body.Bytes()...)51 url := "http://relay.test/r/media/nip96"52 req := httptest.NewRequest(http.MethodPost, url, bytes.NewReader(raw))53 req.Header.Set("Content-Type", form.FormDataContentType())54 signRequest(t, req, string(raw))55 res := httptest.NewRecorder()56 app.ServeHTTP(res, req)57 if res.Code != http.StatusCreated {58 t.Fatalf("upload status=%d body=%s", res.Code, res.Body.String())59 }60 var descriptor struct {61 URL string `json:"url"`62 }63 if err := json.Unmarshal(res.Body.Bytes(), &descriptor); err != nil {64 t.Fatal(err)65 }66 if !strings.Contains(descriptor.URL, "/r/media/") || !strings.HasSuffix(descriptor.URL, ".webp") {67 t.Fatalf("descriptor URL=%q", descriptor.URL)68 }6970 sha := strings.TrimSuffix(strings.TrimPrefix(descriptor.URL, "http://relay.test/r/media/"), ".webp")71 getURL := "http://relay.test/r/media/nip96/" + sha + ".webp"72 get := httptest.NewRequest(http.MethodGet, getURL, nil)73 signRequest(t, get, "")74 getRes := httptest.NewRecorder()75 app.ServeHTTP(getRes, get)76 if getRes.Code != http.StatusOK || getRes.Body.String() != "webp payload" {77 t.Fatalf("get url=%s status=%d body=%q", getURL, getRes.Code, getRes.Body.String())78 }7980 del := httptest.NewRequest(http.MethodDelete, getURL, nil)81 signRequest(t, del, "")82 delRes := httptest.NewRecorder()83 app.ServeHTTP(delRes, del)84 if delRes.Code != http.StatusNoContent {85 t.Fatalf("delete status=%d body=%s", delRes.Code, delRes.Body.String())86 }87 tenant := app.tenants[meta.ID]88 updated := tenant.Policy()89 updated.Features.Files = false90 if err := tenant.applyPolicy(ctx, updated); err != nil {91 t.Fatal(err)92 }93 disabled := httptest.NewRequest(http.MethodGet, getURL, nil)94 disabledRes := httptest.NewRecorder()95 app.ServeHTTP(disabledRes, disabled)96 if disabledRes.Code != http.StatusNotFound {97 t.Fatalf("disabled files status=%d", disabledRes.Code)98 }99100 bad := httptest.NewRequest(http.MethodPost, url, bytes.NewReader(raw))101 bad.Header.Set("Content-Type", form.FormDataContentType())102 signRequest(t, bad, "tampered")103 badRes := httptest.NewRecorder()104 app.ServeHTTP(badRes, bad)105 if badRes.Code == http.StatusCreated || badRes.Code == http.StatusOK {106 t.Fatalf("tampered payload accepted: %d %s", badRes.Code, badRes.Body.String())107 }108}