cdn_stream_test.go 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. package controller
  2. import (
  3. "bytes"
  4. "errors"
  5. "io"
  6. "io/fs"
  7. "net/http"
  8. "net/http/httptest"
  9. "testing"
  10. "testing/fstest"
  11. "git.aetherial.dev/aeth/keiji/pkg/storage"
  12. "github.com/gin-gonic/gin"
  13. )
  14. type trackedImageStream struct {
  15. *bytes.Reader
  16. closed bool
  17. }
  18. func (s *trackedImageStream) Close() error { s.closed = true; return nil }
  19. type streamingRepo struct {
  20. *mediaRepo
  21. stream *trackedImageStream
  22. err error
  23. }
  24. func (r *streamingRepo) OpenImage(id storage.Identifier) (storage.Image, storage.ReadSeekCloser, error) {
  25. return storage.Image{MediaType: "video/webm"}, r.stream, r.err
  26. }
  27. func TestServeImageStream(t *testing.T) {
  28. gin.SetMode(gin.TestMode)
  29. for _, tc := range []struct {
  30. method, rangeHeader string
  31. status int
  32. body string
  33. }{
  34. {"GET", "bytes=2-5", 206, "2345"}, {"HEAD", "", 200, ""}, {"GET", "bytes=20-30", 416, ""},
  35. } {
  36. stream := &trackedImageStream{Reader: bytes.NewReader([]byte("0123456789"))}
  37. repo := &streamingRepo{mediaRepo: &mediaRepo{}, stream: stream}
  38. router := gin.New()
  39. c := &Controller{database: repo}
  40. router.Handle(tc.method, "/images/:file", c.ServeImage)
  41. req := httptest.NewRequest(tc.method, "/images/x", nil)
  42. req.Header.Set("Range", tc.rangeHeader)
  43. res := httptest.NewRecorder()
  44. router.ServeHTTP(res, req)
  45. if res.Code != tc.status || (tc.status != 416 && res.Body.String() != tc.body) || !stream.closed {
  46. t.Fatalf("%s %d %q closed=%v", tc.method, res.Code, res.Body.String(), stream.closed)
  47. }
  48. }
  49. }
  50. type trackingFS struct{ file *trackingFile }
  51. func (f trackingFS) Open(name string) (fs.File, error) { return f.file, nil }
  52. type trackingFile struct {
  53. fs.File
  54. closed bool
  55. fail bool
  56. }
  57. func (f *trackingFile) Close() error { f.closed = true; return f.File.Close() }
  58. func (f *trackingFile) Read(b []byte) (int, error) {
  59. if f.fail {
  60. return 0, errors.New("read failed")
  61. }
  62. return f.File.Read(b)
  63. }
  64. func TestServeGenericClosesFile(t *testing.T) {
  65. gin.SetMode(gin.TestMode)
  66. for _, fail := range []bool{false, true} {
  67. f, err := (fstest.MapFS{"cdn/a.js": &fstest.MapFile{Data: []byte("test")}}).Open("cdn/a.js")
  68. if err != nil {
  69. t.Fatal(err)
  70. }
  71. tf := &trackingFile{File: f, fail: fail}
  72. c := &Controller{FileIO: trackingFS{tf}}
  73. router := gin.New()
  74. router.GET("/cdn/:file", c.ServeGeneric)
  75. res := httptest.NewRecorder()
  76. router.ServeHTTP(res, httptest.NewRequest(http.MethodGet, "/cdn/a.js", nil))
  77. want := 200
  78. if fail {
  79. want = 500
  80. }
  81. if res.Code != want || !tf.closed {
  82. t.Fatalf("status %d closed %v", res.Code, tf.closed)
  83. }
  84. }
  85. }
  86. var _ io.ReadSeeker = (*trackedImageStream)(nil)