all repos

onasty @ 234f764

a one-time notes service

onasty/e2e/apiv1_notes_test.go (view raw)

Olexandr Smirnov Olexandr Smirnov
ss2316544@gmail.com
fix: return "invalid slug" error to user (#189)..., 9 months ago
1
package e2e_test
2
3
import (
4
	"net/http"
5
	"net/http/httptest"
6
	"time"
7
8
	"github.com/gofrs/uuid/v5"
9
	"github.com/olexsmir/onasty/internal/models"
10
)
11
12
type (
13
	apiv1NoteCreateRequest struct {
14
		Content              string    `json:"content"`
15
		Slug                 string    `json:"slug"`
16
		Password             string    `json:"password"`
17
		BurnBeforeExpiration bool      `json:"burn_before_expiration"`
18
		ExpiresAt            time.Time `json:"expires_at"`
19
	}
20
	apiv1NoteCreateResponse struct {
21
		Slug string `json:"slug"`
22
	}
23
)
24
25
func (e *AppTestSuite) TestNoteV1_Create() {
26
	tests := []struct {
27
		name   string
28
		inp    apiv1NoteCreateRequest
29
		assert func(*httptest.ResponseRecorder, apiv1NoteCreateRequest)
30
	}{
31
		{
32
			name: "empty request",
33
			inp:  apiv1NoteCreateRequest{}, //nolint:exhaustruct
34
			assert: func(r *httptest.ResponseRecorder, _ apiv1NoteCreateRequest) {
35
				e.Equal(r.Code, http.StatusBadRequest)
36
			},
37
		},
38
		{
39
			name: "content only",
40
			inp:  apiv1NoteCreateRequest{Content: e.uuid()}, //nolint:exhaustruct
41
			assert: func(r *httptest.ResponseRecorder, _ apiv1NoteCreateRequest) {
42
				e.Equal(r.Code, http.StatusCreated)
43
44
				var body apiv1NoteCreateResponse
45
				e.readBodyAndUnjsonify(r.Body, &body)
46
47
				_, err := uuid.FromString(body.Slug)
48
				e.require.NoError(err)
49
50
				dbNote := e.getNoteBySlug(body.Slug)
51
				e.NotEmpty(dbNote)
52
			},
53
		},
54
		{
55
			name: "set slug",
56
			inp: apiv1NoteCreateRequest{ //nolint:exhaustruct
57
				Slug:    e.uuid() + "fuker",
58
				Content: e.uuid(),
59
			},
60
			assert: func(r *httptest.ResponseRecorder, inp apiv1NoteCreateRequest) {
61
				e.Equal(r.Code, http.StatusCreated)
62
63
				var body apiv1NoteCreateResponse
64
				e.readBodyAndUnjsonify(r.Body, &body)
65
66
				dbNote := e.getNoteBySlug(inp.Slug)
67
				e.NotEmpty(dbNote)
68
			},
69
		},
70
		{
71
			name: "invalid slug, with space",
72
			inp: apiv1NoteCreateRequest{ //nolint:exhaustruct
73
				Slug:    e.uuid() + "fuker fuker",
74
				Content: e.uuid(),
75
			},
76
			assert: func(r *httptest.ResponseRecorder, _ apiv1NoteCreateRequest) {
77
				e.Equal(http.StatusBadRequest, r.Code)
78
			},
79
		},
80
		{
81
			name: "invalid slug, with slash",
82
			inp: apiv1NoteCreateRequest{ //nolint:exhaustruct
83
				Slug:    e.uuid() + "fuker/fuker",
84
				Content: e.uuid(),
85
			},
86
			assert: func(r *httptest.ResponseRecorder, _ apiv1NoteCreateRequest) {
87
				e.Equal(http.StatusBadRequest, r.Code)
88
			},
89
		},
90
		{
91
			name: "invalid slug, 'read'",
92
			inp: apiv1NoteCreateRequest{ //nolint:exhaustruct
93
				Slug:    "read",
94
				Content: e.uuid(),
95
			},
96
			assert: func(r *httptest.ResponseRecorder, _ apiv1NoteCreateRequest) {
97
				e.Equal(r.Code, http.StatusBadRequest)
98
99
				var body errorResponse
100
				e.readBodyAndUnjsonify(r.Body, &body)
101
102
				e.Equal(models.ErrNoteSlugIsAlreadyInUse.Error(), body.Message)
103
			},
104
		},
105
		{
106
			name: "invalid slug, 'unread'",
107
			inp: apiv1NoteCreateRequest{ //nolint:exhaustruct
108
				Slug:    "unread",
109
				Content: e.uuid(),
110
			},
111
			assert: func(r *httptest.ResponseRecorder, _ apiv1NoteCreateRequest) {
112
				e.Equal(r.Code, http.StatusBadRequest)
113
114
				var body errorResponse
115
				e.readBodyAndUnjsonify(r.Body, &body)
116
117
				e.Equal(models.ErrNoteSlugIsAlreadyInUse.Error(), body.Message)
118
			},
119
		},
120
		{
121
			name: "slug provided but empty",
122
			inp: apiv1NoteCreateRequest{ //nolint:exhaustruct
123
				Slug:    "",
124
				Content: e.uuid(),
125
			},
126
			assert: func(r *httptest.ResponseRecorder, inp apiv1NoteCreateRequest) {
127
				e.Equal(r.Code, http.StatusCreated)
128
129
				var body apiv1NoteCreateResponse
130
				e.readBodyAndUnjsonify(r.Body, &body)
131
132
				dbNote := e.getNoteBySlug(body.Slug)
133
				e.NotEmpty(dbNote)
134
				e.Equal(inp.Content, dbNote.Content)
135
			},
136
		},
137
		{
138
			name: "set password",
139
			inp: apiv1NoteCreateRequest{ //nolint:exhaustruct
140
				Content:  e.uuid(),
141
				Password: e.uuid(),
142
			},
143
			assert: func(r *httptest.ResponseRecorder, _ apiv1NoteCreateRequest) {
144
				e.Equal(http.StatusCreated, r.Code)
145
			},
146
		},
147
		{
148
			name: "all possible fields",
149
			inp: apiv1NoteCreateRequest{ //nolint:exhaustruct
150
				Content:              e.uuid(),
151
				BurnBeforeExpiration: true,
152
				ExpiresAt:            time.Now().Add(time.Hour),
153
			},
154
			assert: func(r *httptest.ResponseRecorder, inp apiv1NoteCreateRequest) {
155
				e.Equal(r.Code, http.StatusCreated)
156
157
				var body apiv1NoteCreateResponse
158
				e.readBodyAndUnjsonify(r.Body, &body)
159
160
				dbNote := e.getNoteBySlug(body.Slug)
161
				e.NotEmpty(dbNote)
162
163
				e.Equal(dbNote.Content, inp.Content)
164
				e.Equal(dbNote.BurnBeforeExpiration, inp.BurnBeforeExpiration)
165
				e.Equal(dbNote.ExpiresAt.Unix(), inp.ExpiresAt.Unix())
166
			},
167
		},
168
	}
169
170
	for _, tt := range tests {
171
		httpResp := e.httpRequest(http.MethodPost, "/api/v1/note", e.jsonify(tt.inp))
172
		tt.assert(httpResp, tt.inp)
173
	}
174
}
175
176
type apiv1NoteGetResponse struct {
177
	Content   string     `json:"content"`
178
	ReadAt    *time.Time `json:"read_at"`
179
	CreatedAt time.Time  `json:"created_at"`
180
	ExpiresAt time.Time  `json:"expires_at"`
181
}
182
183
func (e *AppTestSuite) TestNoteV1_Get() {
184
	content := e.uuid()
185
	httpResp := e.httpRequest(
186
		http.MethodPost,
187
		"/api/v1/note",
188
		e.jsonify(apiv1NoteCreateRequest{ //nolint:exhaustruct
189
			Content: content,
190
		}),
191
	)
192
	e.Equal(http.StatusCreated, httpResp.Code)
193
194
	var bodyCreated apiv1NoteCreateResponse
195
	e.readBodyAndUnjsonify(httpResp.Body, &bodyCreated)
196
197
	httpResp = e.httpRequest(http.MethodGet, "/api/v1/note/"+bodyCreated.Slug, nil)
198
	e.Equal(httpResp.Code, http.StatusOK)
199
200
	var body apiv1NoteGetResponse
201
	e.readBodyAndUnjsonify(httpResp.Body, &body)
202
203
	e.Equal(content, body.Content)
204
205
	dbNote := e.getNoteBySlug(bodyCreated.Slug)
206
	e.Equal(dbNote.Content, "")
207
	e.Equal(dbNote.ReadAt.IsZero(), false)
208
}
209
210
type apiv1NoteGetWithPasswordRequest struct {
211
	Password string `json:"password"`
212
}
213
214
func (e *AppTestSuite) TestNoteV1_GetWithPassword() {
215
	content := e.uuid()
216
	passwd := e.uuid()
217
	httpResp := e.httpRequest(
218
		http.MethodPost,
219
		"/api/v1/note",
220
		e.jsonify(apiv1NoteCreateRequest{ //nolint:exhaustruct
221
			Content:  content,
222
			Password: passwd,
223
		}),
224
	)
225
	e.Equal(http.StatusCreated, httpResp.Code)
226
227
	var bodyCreated apiv1NoteCreateResponse
228
	e.readBodyAndUnjsonify(httpResp.Body, &bodyCreated)
229
230
	httpResp = e.httpRequest(
231
		http.MethodPost,
232
		"/api/v1/note/"+bodyCreated.Slug+"/view",
233
		e.jsonify(apiv1NoteGetWithPasswordRequest{
234
			Password: passwd,
235
		}),
236
	)
237
	e.Equal(httpResp.Code, http.StatusOK)
238
239
	var body apiv1NoteGetResponse
240
	e.readBodyAndUnjsonify(httpResp.Body, &body)
241
242
	e.Equal(content, body.Content)
243
244
	dbNote := e.getNoteBySlug(bodyCreated.Slug)
245
	e.Equal(dbNote.Content, "")
246
	e.Equal(dbNote.ReadAt.IsZero(), false)
247
}
248
249
func (e *AppTestSuite) TestNoteV1_GetWithPassword_wrongNoPassword() {
250
	content := e.uuid()
251
	passwd := e.uuid()
252
	httpResp := e.httpRequest(
253
		http.MethodPost,
254
		"/api/v1/note",
255
		e.jsonify(apiv1NoteCreateRequest{ //nolint:exhaustruct
256
			Content:  content,
257
			Password: passwd,
258
		}),
259
	)
260
	e.Equal(http.StatusCreated, httpResp.Code)
261
262
	var bodyCreated apiv1NoteCreateResponse
263
	e.readBodyAndUnjsonify(httpResp.Body, &bodyCreated)
264
265
	httpResp = e.httpRequest(http.MethodGet, "/api/v1/note/"+bodyCreated.Slug, nil)
266
	e.Equal(httpResp.Code, http.StatusNotFound)
267
}
268
269
func (e *AppTestSuite) TestNoteV1_GetWithPassword_wrong() {
270
	content := e.uuid()
271
	httpResp := e.httpRequest(
272
		http.MethodPost,
273
		"/api/v1/note",
274
		e.jsonify(apiv1NoteCreateRequest{ //nolint:exhaustruct
275
			Content:  content,
276
			Password: e.uuid(),
277
		}),
278
	)
279
	e.Equal(http.StatusCreated, httpResp.Code)
280
281
	var bodyCreated apiv1NoteCreateResponse
282
	e.readBodyAndUnjsonify(httpResp.Body, &bodyCreated)
283
284
	httpResp = e.httpRequest(
285
		http.MethodPost,
286
		"/api/v1/note/"+bodyCreated.Slug+"/view",
287
		e.jsonify(apiv1NoteGetWithPasswordRequest{
288
			Password: e.uuid(),
289
		}),
290
	)
291
	e.Equal(httpResp.Code, http.StatusNotFound)
292
}
293
294
type apiv1NoteMetadataResponse struct {
295
	CreatedAt   time.Time `json:"created_at"`
296
	HasPassword bool      `json:"has_password"`
297
}
298
299
func (e *AppTestSuite) TestNoteV1_GetMetadata() {
300
	// create note
301
	httpResp := e.httpRequest(
302
		http.MethodPost,
303
		"/api/v1/note",
304
		e.jsonify(apiv1NoteCreateRequest{Content: "content"}), //nolint:exhaustruct
305
	)
306
	e.Equal(http.StatusCreated, httpResp.Code)
307
308
	var bodyCreated apiv1NoteCreateResponse
309
	e.readBodyAndUnjsonify(httpResp.Body, &bodyCreated)
310
311
	// get metadata
312
	metaResp := e.httpRequest(http.MethodGet, "/api/v1/note/"+bodyCreated.Slug+"/meta", []byte{})
313
	e.Equal(metaResp.Code, http.StatusOK)
314
315
	var metadata apiv1NoteMetadataResponse
316
	e.readBodyAndUnjsonify(metaResp.Body, &metadata)
317
318
	e.False(metadata.HasPassword)
319
	e.NotEmpty(metadata.CreatedAt)
320
}
321
322
func (e *AppTestSuite) TestNoteV1_GetMetadata_withPassword() {
323
	// create note
324
	httpResp := e.httpRequest(
325
		http.MethodPost,
326
		"/api/v1/note",
327
		e.jsonify(apiv1NoteCreateRequest{ //nolint:exhaustruct
328
			Content:  "content",
329
			Password: "pass",
330
		}),
331
	)
332
	e.Equal(http.StatusCreated, httpResp.Code)
333
334
	var bodyCreated apiv1NoteCreateResponse
335
	e.readBodyAndUnjsonify(httpResp.Body, &bodyCreated)
336
337
	// get metadata
338
	metaResp := e.httpRequest(http.MethodGet, "/api/v1/note/"+bodyCreated.Slug+"/meta", []byte{})
339
	e.Equal(http.StatusOK, metaResp.Code)
340
341
	var metadata apiv1NoteMetadataResponse
342
	e.readBodyAndUnjsonify(metaResp.Body, &metadata)
343
344
	e.True(metadata.HasPassword)
345
	e.NotEmpty(metadata.CreatedAt)
346
}
347
348
func (e *AppTestSuite) TestNoteV1_GetMetadata_notFound() {
349
	metaResp := e.httpRequest(http.MethodGet, "/api/v1/note/"+e.uuid()+"/meta", []byte{})
350
	e.Equal(http.StatusNotFound, metaResp.Code)
351
}
352
353
func (e *AppTestSuite) TestNoteV1_GetMetadata_readNote() {
354
	// create note
355
	createdResp := e.httpRequest(
356
		http.MethodPost,
357
		"/api/v1/note",
358
		e.jsonify(apiv1NoteCreateRequest{Content: "content"}), //nolint:exhaustruct
359
	)
360
	e.Equal(http.StatusCreated, createdResp.Code)
361
362
	var bodyCreated apiv1NoteCreateResponse
363
	e.readBodyAndUnjsonify(createdResp.Body, &bodyCreated)
364
365
	// read note
366
	readResp := e.httpRequest(http.MethodGet, "/api/v1/note/"+bodyCreated.Slug, nil)
367
	e.Equal(http.StatusOK, readResp.Code)
368
369
	// get metadata
370
	metaResp := e.httpRequest(http.MethodGet, "/api/v1/note/"+e.uuid()+"/meta", nil)
371
	e.Equal(http.StatusNotFound, metaResp.Code)
372
}