github.com/k0marov/go-socnet@v0.0.0-20220715154813-90d07867c782/features/posts/integration_test/posts_test.go (about) 1 package posts_test 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "github.com/k0marov/go-socnet/core/general/core_values" 8 "github.com/k0marov/go-socnet/core/general/static_store" 9 helpers "github.com/k0marov/go-socnet/core/helpers/http_test_helpers" 10 . "github.com/k0marov/go-socnet/core/helpers/test_helpers" 11 "log" 12 "mime/multipart" 13 "net/http" 14 "net/http/httptest" 15 "os" 16 "path/filepath" 17 "strconv" 18 "testing" 19 20 "github.com/go-chi/chi/v5" 21 "github.com/k0marov/go-socnet/features/posts" 22 "github.com/k0marov/go-socnet/features/posts/delivery/http/responses" 23 "github.com/k0marov/go-socnet/features/posts/domain/values" 24 post_storage "github.com/k0marov/go-socnet/features/posts/store/file_storage" 25 "github.com/k0marov/go-socnet/features/profiles" 26 profile_entities "github.com/k0marov/go-socnet/features/profiles/domain/entities" 27 profile_models "github.com/k0marov/go-socnet/features/profiles/domain/models" 28 auth "github.com/k0marov/golang-auth" 29 _ "github.com/mattn/go-sqlite3" 30 ) 31 32 func TestPosts(t *testing.T) { 33 // working directory 34 os.Mkdir("tmp_test", 0777) 35 os.Chdir("tmp_test") 36 defer func() { 37 os.Chdir("..") 38 os.RemoveAll("tmp_test") 39 }() 40 41 // db 42 sql := OpenSqliteDB(t) 43 44 r := chi.NewRouter() 45 // profiles 46 r.Route("/profiles", profiles.NewProfilesRouterImpl(sql)) 47 fakeRegisterProfile := profiles.NewRegisterCallback(sql) 48 // posts 49 r.Route("/posts", posts.NewPostsRouterImpl(sql, profiles.NewProfileGetterImpl(sql))) 50 51 // helpers 52 createPost := func(t testing.TB, author auth.User, images [][]byte, text string) { 53 t.Helper() 54 body := bytes.NewBuffer(nil) 55 writer := multipart.NewWriter(body) 56 57 writer.WriteField("text", text) 58 for i, image := range images { 59 fw, _ := writer.CreateFormFile(fmt.Sprintf("image_%d", i+1), RandomString()) 60 fw.Write(image) 61 } 62 writer.Close() 63 request := helpers.AddAuthDataToRequest(httptest.NewRequest(http.MethodPost, "/posts", body), author) 64 request.Header.Set("Content-Type", writer.FormDataContentType()) 65 66 response := httptest.NewRecorder() 67 r.ServeHTTP(response, request) 68 AssertStatusCode(t, response, http.StatusOK) 69 } 70 getPosts := func(t testing.TB, author core_values.UserId, caller auth.User) []responses.PostResponse { 71 t.Helper() 72 request := helpers.AddAuthDataToRequest(httptest.NewRequest(http.MethodGet, "/posts/?profile_id="+author, nil), caller) 73 response := httptest.NewRecorder() 74 r.ServeHTTP(response, request) 75 AssertStatusCode(t, response, http.StatusOK) 76 var posts responses.PostsResponse 77 json.NewDecoder(response.Body).Decode(&posts) 78 return posts.Posts 79 } 80 assertImageCreated := func(t testing.TB, post responses.PostResponse, postImage responses.PostImageResponse, wantImage []byte) { 81 t.Helper() 82 path := filepath.Join(static_store.StaticDir, post_storage.GetPostDir(post.Id, post.Author.Id), post_storage.ImagePrefix+strconv.Itoa(postImage.Index)) 83 got := readFile(t, path) 84 Assert(t, got, wantImage, "the stored image data") 85 } 86 deletePost := func(t testing.TB, postId values.PostId, author auth.User) { 87 t.Helper() 88 request := helpers.AddAuthDataToRequest(httptest.NewRequest(http.MethodDelete, "/posts/"+postId, nil), author) 89 response := httptest.NewRecorder() 90 r.ServeHTTP(response, request) 91 AssertStatusCode(t, response, http.StatusOK) 92 } 93 assertPostFilesDeleted := func(t testing.TB, postId values.PostId, author core_values.UserId) { 94 t.Helper() 95 postPath := filepath.Join(static_store.StaticDir, post_storage.GetPostDir(postId, author)) 96 _, err := os.ReadDir(postPath) 97 AssertSomeError(t, err) 98 } 99 toggleLike := func(t testing.TB, postId values.PostId, caller auth.User) { 100 t.Helper() 101 request := helpers.AddAuthDataToRequest(httptest.NewRequest(http.MethodPost, "/posts/"+postId+"/toggle-like", nil), caller) 102 response := httptest.NewRecorder() 103 r.ServeHTTP(response, request) 104 AssertStatusCode(t, response, http.StatusOK) 105 } 106 107 registerProfile := func(user auth.User) profile_entities.Profile { 108 fakeRegisterProfile(user) 109 return profile_entities.Profile{ 110 ProfileModel: profile_models.ProfileModel{ 111 Id: user.Id, 112 Username: user.Username, 113 }, 114 } 115 } 116 117 // create 2 profiles 118 user1 := RandomAuthUser() 119 user2 := RandomAuthUser() 120 registerProfile(user1) 121 registerProfile(user2) 122 123 t.Run("creating, reading and deleting posts", func(t *testing.T) { 124 125 // create post (without images) belonging to 2-nd profile 126 text1 := "Hello, World!" 127 createPost(t, user2, [][]byte{}, text1) 128 129 // create post (with images) belonging to 2-nd profile 130 text2 := "Hello, World with Images!" 131 image1 := readFixture(t, "test_image.jpg") 132 image2 := readFixture(t, "test_image.jpg") 133 createPost(t, user2, [][]byte{image1, image2}, text2) 134 135 // assert they were created 136 posts := getPosts(t, user2.Id, user2) 137 138 AssertFatal(t, len(posts), 2, "number of created posts") 139 140 log.Print(fmt.Sprintf("first: %+v, \nsecond: %+v", posts[0], posts[1])) 141 142 Assert(t, posts[0].Text, text1, "the first post's text") 143 Assert(t, posts[0].Author.Id, user2.Id, "first post's author") 144 AssertFatal(t, len(posts[0].Images), 0, "number of images in first post") 145 146 Assert(t, posts[1].Text, text2, "the second post's text") 147 Assert(t, posts[1].Author.Id, user2.Id, "second posts's author") 148 AssertFatal(t, len(posts[1].Images), 2, "number of images in second post") 149 assertImageCreated(t, posts[1], posts[1].Images[0], image1) 150 assertImageCreated(t, posts[1], posts[1].Images[1], image2) 151 // delete them 152 deletePost(t, posts[0].Id, user2) 153 deletePost(t, posts[1].Id, user2) 154 // assert they were deleted 155 nowPosts := getPosts(t, user2.Id, user2) 156 Assert(t, len(nowPosts), 0, "number of posts after deletion") 157 assertPostFilesDeleted(t, posts[0].Id, user2.Id) 158 assertPostFilesDeleted(t, posts[1].Id, user2.Id) 159 }) 160 t.Run("liking posts", func(t *testing.T) { 161 // create a post belonging to 1-st profile 162 createPost(t, user1, [][]byte{}, "") 163 posts := getPosts(t, user1.Id, user1) 164 165 // like it from 2-nd profile 166 toggleLike(t, posts[0].Id, user2) 167 // assert it is liked 168 posts = getPosts(t, user1.Id, user2) 169 Assert(t, posts[0].IsLiked, true, "post is liked") 170 171 // unlike it from 2-nd profile 172 toggleLike(t, posts[0].Id, user2) 173 // assert it is not liked 174 posts = getPosts(t, user1.Id, user2) 175 Assert(t, posts[0].IsLiked, false, "post is not liked") 176 }) 177 } 178 179 func readFixture(t testing.TB, filename string) []byte { 180 t.Helper() 181 return readFile(t, filepath.Join("..", "testdata", filename)) // ".." since we change the working directory to tmp_test 182 } 183 184 func readFile(t testing.TB, filepath string) []byte { 185 t.Helper() 186 data, err := os.ReadFile(filepath) 187 if err != nil { 188 t.Fatalf("error while reading file %s: %v", filepath, err) 189 } 190 return data 191 }