]> git.giacomolli.uk - onechat/blob - image_test.go
Accept owner photos and image files in Telegram
[onechat] / image_test.go
1 package main
2
3 import (
4         "bytes"
5         "crypto/sha256"
6         "encoding/hex"
7         "os"
8         "path/filepath"
9         "sync"
10         "testing"
11
12         "github.com/stretchr/testify/require"
13 )
14
15 func TestImagesArePrivateSharedAndWrittenOnce(t *testing.T) {
16         root, err := openAgent(filepath.Join(t.TempDir(), "agent"))
17         require.NoError(t, err)
18
19         child := &Agent{
20                 dir:    filepath.Join(root.dir, "children", "House"),
21                 config: AgentConfig{Parent: root.config.Name},
22         }
23         require.Equal(t, root.Images().dir, child.Images().dir)
24
25         data := imageTestPNG(t, 3, 2)
26         sum := sha256.Sum256(data)
27         expected := hex.EncodeToString(sum[:]) + ".png"
28         store := root.Images()
29         name, err := store.Save(data)
30         require.NoError(t, err)
31         require.Equal(t, expected, name)
32
33         info, err := os.Stat(filepath.Join(store.dir, name))
34         require.NoError(t, err)
35         require.Equal(t, os.FileMode(0o600), info.Mode().Perm())
36
37         var workers sync.WaitGroup
38
39         for range 8 {
40                 workers.Go(func() {
41                         saved, saveErr := child.Images().Save(data)
42                         require.NoError(t, saveErr)
43                         require.Equal(t, name, saved)
44                 })
45         }
46
47         workers.Wait()
48
49         again, err := os.Stat(filepath.Join(store.dir, name))
50         require.NoError(t, err)
51         require.True(t, os.SameFile(info, again))
52
53         files, err := os.ReadDir(store.dir)
54         require.NoError(t, err)
55         require.Len(t, files, 1)
56
57         actual, err := store.Read(name)
58         require.NoError(t, err)
59         require.Equal(t, data, actual)
60 }
61
62 func TestImageStoreRejectsLargeUnsupportedAndUnsafeImages(t *testing.T) {
63         store := &ImageStore{dir: filepath.Join(t.TempDir(), imageDirName)}
64         _, err := store.Save(bytes.Repeat([]byte{0}, imageMaxBytes+1))
65         require.ErrorContains(t, err, "exceeds 20 MB")
66
67         _, err = store.Save([]byte("not an image"))
68         require.ErrorContains(t, err, "JPEG, PNG, or WebP")
69
70         _, err = store.Read("../secret.png")
71         require.ErrorContains(t, err, "invalid image name")
72
73         _, err = os.Stat(store.dir)
74         require.ErrorIs(t, err, os.ErrNotExist)
75 }
76
77 func TestBackupKeepsImagesAndTheirModes(t *testing.T) {
78         dir := t.TempDir()
79         store := &ImageStore{dir: filepath.Join(dir, imageDirName)}
80         data := imageTestPNG(t, 2, 2)
81         name, err := store.Save(data)
82         require.NoError(t, err)
83         require.NoError(t, os.WriteFile(filepath.Join(store.dir, ".image-incomplete"), []byte("partial"), 0o600))
84
85         stage := t.TempDir()
86         archive := filepath.Join(t.TempDir(), "images.tar.gz")
87         require.NoError(t, backupCopy(t.Context(), dir, stage, archive))
88
89         _, err = backupArchive(t.Context(), stage, archive)
90         require.NoError(t, err)
91
92         restored := t.TempDir()
93         require.NoError(t, backupRestore(t.Context(), archive, restored))
94
95         actual, err := (&ImageStore{dir: filepath.Join(restored, imageDirName)}).Read(name)
96         require.NoError(t, err)
97         require.Equal(t, data, actual)
98
99         info, err := os.Stat(filepath.Join(restored, imageDirName, name))
100         require.NoError(t, err)
101         require.Equal(t, os.FileMode(0o600), info.Mode().Perm())
102
103         _, err = os.Stat(filepath.Join(restored, imageDirName, ".image-incomplete"))
104         require.ErrorIs(t, err, os.ErrNotExist)
105 }
106
107 func TestImageMarkersKeepCaptionAndOnlySafeNames(t *testing.T) {
108         name := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.png"
109         text := imageText([]string{name}, "Read the label.")
110         require.Equal(t, "[image "+name+"]\nRead the label.", text)
111         require.Equal(t, []string{name}, imageFiles(text+"\n[image ../secret.png]\ntext [image "+name+"]"))
112 }