diff --git a/contact_test.go b/contact_test.go index e95b73d..0a0cdb8 100644 --- a/contact_test.go +++ b/contact_test.go @@ -19,13 +19,13 @@ func TestContact(t *testing.T) { h := NewHandler(db, nil) r := NewRouter(h) - users := setupUsers(t, db, r) + users := setupContactUsers(t, db, r) t.Run("Create", testCreateContact(db, r, users)) t.Run("Get", testGetContacts(db, r, users)) } -func setupUsers(t *testing.T, db *sql.DB, router http.Handler) []User { +func setupContactUsers(t *testing.T, db *sql.DB, router http.Handler) []User { users := []User{ User{ diff --git a/conversation.go b/conversation.go index 58ee678..15be6a1 100644 --- a/conversation.go +++ b/conversation.go @@ -104,13 +104,13 @@ func (h *Handler) GetConversations(w http.ResponseWriter, r *http.Request, p htt // Scan for rows.Next() { - var id, title, picture string - if err := rows.Scan(&id, &title, &picture); err != nil { + conversation := Conversation{} + if err := rows.Scan(&conversation.ID, &conversation.Title, &conversation.Picture, &conversation.Pinned); err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) log.Print(err) return } - conversations = append(conversations, Conversation{ID: id, Title: title, DM: false, Picture: picture}) + conversations = append(conversations, conversation) } // Respond @@ -136,7 +136,7 @@ func (h *Handler) GetConversation(w http.ResponseWriter, r *http.Request, p http member.pinned FROM "conversation", member WHERE member.conversation = "conversation".id AND member.user = $1 AND member.conversation = $2 - `, userID, conversationID).Scan(&conversation.ID, &conversation.Title, &conversation.Picture) + `, userID, conversationID).Scan(&conversation.ID, &conversation.Title, &conversation.Picture, &conversation.Pinned) switch { case err == sql.ErrNoRows: @@ -183,7 +183,7 @@ func (h *Handler) UpdateConversation(w http.ResponseWriter, r *http.Request, p h } // Update - if len(conversation.Title) > 0 { + if conversation.Title.Valid { _, err = h.db.Exec(` UPDATE "conversation" SET title = $2, picture = $3 @@ -429,13 +429,13 @@ func (h *Handler) GetConversationMembers(w http.ResponseWriter, r *http.Request, // Scan for rows.Next() { - var id, username, bio, profilePic, firstName, lastName, phoneNumber string - if err := rows.Scan(&id, &username, &bio, &profilePic, &firstName, &lastName, &phoneNumber); err != nil { + user := User{} + if err := rows.Scan(&user.ID, &user.Username, &user.Bio, &user.ProfilePic, &user.FirstName, &user.LastName, &user.PhoneNumber); err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) log.Print(err) return } - users = append(users, User{ID: id, Username: &username, Bio: bio, ProfilePic: profilePic, FirstName: firstName, LastName: lastName, PhoneNumber: phoneNumber}) + users = append(users, user) } // Respond diff --git a/conversation_test.go b/conversation_test.go index 7b5d10e..1f34982 100644 --- a/conversation_test.go +++ b/conversation_test.go @@ -11,6 +11,7 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "gopkg.in/guregu/null.v3" ) func TestConversation(t *testing.T) { @@ -19,13 +20,13 @@ func TestConversation(t *testing.T) { h := NewHandler(db, nil) r := NewRouter(h) - users := setupUsers(t, db, r) + users := setupConversationUsers(t, db, r) t.Run("Create", testCreateConversation(db, r, users)) t.Run("Get", testGetConversations(db, r, users)) } -func setupUsers(t *testing.T, db *sql.DB, router http.Handler) []User { +func setupConversationUsers(t *testing.T, db *sql.DB, router http.Handler) []User { users := []User{ User{ @@ -68,31 +69,30 @@ func setupUsers(t *testing.T, db *sql.DB, router http.Handler) []User { func testCreateConversation(db *sql.DB, router http.Handler, users []User) func(t *testing.T) { return func(t *testing.T) { - // Setup - // Test mockConversation := &Conversation{ - Title: "Test Conversation 1", - DM: true, + Title: null.StringFrom("Test Conversation 1"), + DM: false, } b, _ := json.Marshal(mockConversation) w := httptest.NewRecorder() r := httptest.NewRequest("POST", "/user/conversation", bytes.NewBuffer(b)) - claim, _ := json.Marshal(&RawClient{UserId: createdUser.ID, ClientId: "test"}) + claim, _ := json.Marshal(&RawClient{UserId: users[0].ID, ClientId: "test"}) r.Header.Add("X-User-Claim", string(claim)) router.ServeHTTP(w, r) assertCode(t, w, 200) // Assert - got, want := User{}, users[0] + got, want := &Conversation{}, mockConversation json.NewDecoder(w.Body).Decode(&got) - if diff := cmp.Diff(got, want); len(diff) != 0 { - t.Error(diff) + if got.DM != want.DM || got.Title.String != want.Title.String { + t.Error("Wanted a Conversation with same Title, DM. Got something else") } - assertDB(t, db, `SELECT * FROM contact WHERE "user" = $1 AND contact = $2`, createdUser.ID, users[0].ID) + assertDB(t, db, `SELECT * FROM "conversation" WHERE title = $1 AND dm = $2`, mockConversation.Title, mockConversation.DM) + assertDB(t, db, `SELECT * FROM member WHERE "user" = $1 AND "conversation" = $2`, users[0].ID, got.ID) } } @@ -101,59 +101,34 @@ func testGetConversations(db *sql.DB, router http.Handler, users []User) func(t return func(t *testing.T) { // Setup - mockUser := &User{ - PhoneNumber: "+65 9999 1002", - FirstName: "ConversationOwner", - LastName: "User", + mockConversation := &Conversation{ + Title: null.StringFrom("Test Conversation 2"), + DM: false, } - bs, _ := json.Marshal(mockUser) + bs, _ := json.Marshal(mockConversation) ws := httptest.NewRecorder() - rs := httptest.NewRequest("POST", "/user", bytes.NewBuffer(bs)) + rs := httptest.NewRequest("POST", "/user/conversation", bytes.NewBuffer(bs)) + claims, _ := json.Marshal(&RawClient{UserId: users[1].ID, ClientId: "test"}) + rs.Header.Add("X-User-Claim", string(claims)) + router.ServeHTTP(ws, rs) - - createdUser := new(User) - json.NewDecoder(ws.Body).Decode(createdUser) - - b := []byte(`{"phone_number": "` + users[0].PhoneNumber + `"}`) - - w := httptest.NewRecorder() - r := httptest.NewRequest("POST", "/user/contact", bytes.NewBuffer(b)) - claim, _ := json.Marshal(&RawClient{UserId: createdUser.ID, ClientId: "test"}) - r.Header.Add("X-User-Claim", string(claim)) - - router.ServeHTTP(w, r) - assertCode(t, w, 200) - - b = []byte(`{"phone_number": "` + users[1].PhoneNumber + `"}`) - - w = httptest.NewRecorder() - r = httptest.NewRequest("POST", "/user/contact", bytes.NewBuffer(b)) - r.Header.Add("X-User-Claim", string(claim)) - - router.ServeHTTP(w, r) - assertCode(t, w, 200) - - b = []byte(`{"phone_number": "` + users[2].PhoneNumber + `"}`) - - w = httptest.NewRecorder() - r = httptest.NewRequest("POST", "/user/contact", bytes.NewBuffer(b)) - r.Header.Add("X-User-Claim", string(claim)) - - router.ServeHTTP(w, r) - assertCode(t, w, 200) + assertCode(t, ws, 200) // Test - w = httptest.NewRecorder() - r = httptest.NewRequest("GET", "/user/contact", nil) + w := httptest.NewRecorder() + r := httptest.NewRequest("GET", "/user/conversation", nil) + claim, _ := json.Marshal(&RawClient{UserId: users[1].ID, ClientId: "test"}) r.Header.Add("X-User-Claim", string(claim)) router.ServeHTTP(w, r) assertCode(t, w, 200) + conversations := make([]Conversation, 1) + json.NewDecoder(w.Body).Decode(&conversations) // Assert - got, want := []User{}, users - json.NewDecoder(w.Body).Decode(&got) + got, want := conversations[0], Conversation{} + json.NewDecoder(ws.Body).Decode(&want) if diff := cmp.Diff(got, want); len(diff) != 0 { t.Error(diff) } diff --git a/go.mod b/go.mod index c7975ee..e0e506c 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect github.com/ttacon/libphonenumber v1.0.0 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect + gopkg.in/guregu/null.v3 v3.4.0 ) go 1.13 diff --git a/go.sum b/go.sum index 23777db..f80cf08 100644 --- a/go.sum +++ b/go.sum @@ -27,3 +27,5 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/guregu/null.v3 v3.4.0 h1:AOpMtZ85uElRhQjEDsFx21BkXqFPwA7uoJukd4KErIs= +gopkg.in/guregu/null.v3 v3.4.0/go.mod h1:E4tX2Qe3h7QdL+uZ3a0vqvYwKQsRSQKM5V4YltdgH9Y= diff --git a/types.go b/types.go index 688cc70..a5986a9 100644 --- a/types.go +++ b/types.go @@ -1,6 +1,6 @@ package main -// String pointer means nullable +import "gopkg.in/guregu/null.v3" type UpdateMsg struct { Type string `json:"type"` @@ -19,21 +19,21 @@ type Member struct { } type Conversation struct { - ID string `json:"id"` // id - Title string `json:"title"` // title - DM bool `json:"dm"` // dm - Picture string `json:"picture"` // picture - Pinned bool `json:"pinned"` // pinned + ID string `json:"id"` // id + Title null.String `json:"title"` // title + DM bool `json:"dm"` // dm + Picture null.String `json:"picture"` // picture + Pinned bool `json:"pinned"` // pinned } type User struct { - ID string `json:"id"` // id - Username *string `json:"username"` // username - Bio string `json:"bio"` // bio - ProfilePic string `json:"profile_pic"` // profile_pic - FirstName string `json:"first_name"` // first_name - LastName string `json:"last_name"` // last_name - PhoneNumber string `json:"phone_number"` // phone_number + ID string `json:"id"` // id + Username null.String `json:"username"` // username + Bio string `json:"bio"` // bio + ProfilePic string `json:"profile_pic"` // profile_pic + FirstName string `json:"first_name"` // first_name + LastName string `json:"last_name"` // last_name + PhoneNumber string `json:"phone_number"` // phone_number } type PhoneNumber struct {