oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

1package sheets
2
3import (
4 "encoding/json"
5 "net/http"
6 "net/http/httptest"
7 "strconv"
8 "strings"
9 "testing"
10 "time"
11)
12
13func nativeGet(path string) *http.Request {
14 r := httptest.NewRequest("GET", path, nil)
15 r.Header.Set("Accept", "application/json")
16 return r
17}
18func TestNativeSheetPayloadUsesExistingPermissions(t *testing.T) {
19 a, cookies := signIn(t)
20 s, cells := newSheet(t, a, cookies, "Native secret title")
21 postCell(t, a, cookies, cells, `{"r":0,"c":0,"raw":"=2+3"}`)
22 for _, path := range []string{"/sheets", "/sheets/" + strconv.FormatUint(s.ID, 10), "/sheets/" + strconv.FormatUint(s.ID, 10) + "/history", "/sheets/" + strconv.FormatUint(s.ID, 10) + "/comments", "/sheets/" + strconv.FormatUint(s.ID, 10) + "/table"} {
23 rec := do(t, a, as(nativeGet(path), cookies))
24 if rec.Code != 200 || !json.Valid(rec.Body.Bytes()) || rec.Header().Get("Cache-Control") != "no-store" {
25 t.Fatalf("native payload %s: %d", path, rec.Code)
26 }
27 if strings.Contains(rec.Body.String(), "<svg") {
28 t.Fatal("native payload includes HTML thumbnail")
29 }
30 }
31 rec := do(t, a, nativeGet("/sheets/"+strconv.FormatUint(s.ID, 10)))
32 if rec.Code == 200 {
33 t.Fatal("anonymous native sheet read succeeded")
34 }
35}
36func TestNativeListPreviewContainsShapeWithoutCellContents(t *testing.T) {
37 a, cookies := signIn(t)
38 _, cells := newSheet(t, a, cookies, "Workbook")
39 secret := "private-cell-preview-sentinel"
40 postCell(t, a, cookies, cells, `{"r":0,"c":0,"raw":"`+secret+`"}`)
41 rec := do(t, a, as(nativeGet("/sheets"), cookies))
42 if rec.Code != 200 || strings.Contains(rec.Body.String(), secret) {
43 t.Fatal("native preview leaked a cell or failed")
44 }
45 var payload struct{ Sheets []struct{ Preview preview } }
46 if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil {
47 t.Fatal(err)
48 }
49 if len(payload.Sheets) != 1 || payload.Sheets[0].Preview.Empty || len(payload.Sheets[0].Preview.Cells) != 1 {
50 t.Fatal("native preview did not include the real cell shape")
51 }
52 if rec := do(t, a, nativeGet("/sheets")); rec.Code == 200 {
53 t.Fatal("unsigned list exposed previews")
54 }
55}
56
57func TestNativeCreateAndHistoryCommandsAnswerJSON(t *testing.T) {
58 a, cookies := signIn(t)
59 r := postForm("/sheets", map[string][]string{"title": {"Created natively"}})
60 r.Header.Set("Accept", "application/json")
61 rec := do(t, a, as(r, cookies))
62 if rec.Code != 201 {
63 t.Fatalf("create=%d", rec.Code)
64 }
65 var s SheetSummary
66 if err := json.Unmarshal(rec.Body.Bytes(), &s); err != nil {
67 t.Fatal(err)
68 }
69 path := "/sheets/" + strconv.FormatUint(s.ID, 10)
70 r = postForm(path+"/rename", map[string][]string{"title": {"Renamed natively"}})
71 r.Header.Set("Accept", "application/json")
72 rec = do(t, a, as(r, cookies))
73 if rec.Code != 200 || !json.Valid(rec.Body.Bytes()) {
74 t.Fatalf("rename=%d", rec.Code)
75 }
76 r = postForm(path+"/undo", nil)
77 r.Header.Set("Accept", "application/json")
78 rec = do(t, a, as(r, cookies))
79 if !strings.Contains(rec.Body.String(), `"nothing"`) {
80 t.Fatalf("undo did not preserve notice: %s", rec.Body.String())
81 }
82}
83func TestNativeNegotiationDoesNotExposeAuthPayloads(t *testing.T) {
84 a, _ := signIn(t)
85 r := nativeGet("/signin")
86 rec := do(t, a, r)
87 if strings.Contains(rec.Header().Get("Content-Type"), "application/json") {
88 t.Fatal("auth page was serialized")
89 }
90}
91
92func TestNativeStreamRechecksRevokedSession(t *testing.T) {
93 a, cookies := signIn(t)
94 s, _ := newSheet(t, a, cookies, "Private stream")
95 srv := liveServer(t, a)
96 response := openStream(t, srv, s, cookies)
97 if response.StatusCode != 200 {
98 t.Fatal(response.StatusCode)
99 }
100 if err := a.db.Exec("DELETE FROM sessions").Error; err != nil {
101 t.Fatal(err)
102 }
103 a.publish(s.TabID, gridResponse{Version: 1, Cells: []gridCell{{R: 0, C: 0, Raw: "must-not-leak", Display: "must-not-leak"}}})
104 event := readEventWithin(t, response.Body, 2*time.Second)
105 if !strings.Contains(event, "access-changed") || strings.Contains(event, "must-not-leak") {
106 t.Fatalf("unexpected event: %s", event)
107 }
108}
109
110func TestNativeGridWindowKeepsOriginalCoordinates(t *testing.T) {
111 a, cookies := signIn(t)
112 _, cells := newSheet(t, a, cookies, "Window")
113 postCell(t, a, cookies, cells, `{"r":30,"c":27,"raw":"=7*8"}`)
114 rec := do(t, a, as(nativeGet(cells+"?firstRow=25&firstCol=26&rows=25&cols=25"), cookies))
115 value := decodeGrid(t, rec)
116 if len(value.Cells) != 1 || value.Cells[0].R != 30 || value.Cells[0].C != 27 || value.Cells[0].Display != "56" {
117 t.Fatalf("window lost source coordinate: %+v", value.Cells)
118 }
119 rec = do(t, a, as(nativeGet(cells+"?firstRow=-1"), cookies))
120 if rec.Code != 400 {
121 t.Fatal("negative offset accepted")
122 }
123}
124