4
1
Fork 0

Fix conversations, add integration tests
continuous-integration/drone/pr Build is passing Details

pull/24/head
Ambrose Chua 2019-10-23 22:51:25 +08:00
parent f03de17d64
commit d7a27e5c52
Signed by: ambrose
GPG Key ID: BC367D33F140B5C2
6 changed files with 53 additions and 75 deletions

View File

@ -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{

View File

@ -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

View File

@ -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)
}

1
go.mod
View File

@ -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

2
go.sum
View File

@ -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=

View File

@ -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 {