diff --git a/Makefile b/Makefile index bf30481..8f98bc0 100644 --- a/Makefile +++ b/Makefile @@ -15,30 +15,37 @@ BINARY_NAME_CONSUMER=consumer.out BINARY_UNIX=$(BINARY_NAME)_unix GOOGLE_GCR_HOSTNAME=gcr.io GOOGLE_PROJECT_ID=spatiumsocialis +SOURCE_ENV=source .env + + +ifeq ($(env),) +env := dev +endif all: deps test build # TODO: Clean this mess up test: - $(GOTEST) -coverprofile=/tmp/coverage.out ./$(package)... $(ARGS) - go tool cover -html=/tmp/coverage.out + $(SOURCE_ENV) && $(GOTEST) -coverprofile=/tmp/coverage.out ./pkg/... $(ARGS) .PHONY: test +coverage: + go tool cover -html=/tmp/coverage.out build-token: $(GOBUILD) -o ./tools/tokengen/cmd/tokengen/tokengen.out ./tools/tokengen/cmd/tokengen token: - ./tools/tokengen/cmd/tokengen/tokengen.out -u $(uid) + $(SOURCE_ENV) && ./tools/tokengen/cmd/tokengen/tokengen.out -u $(uid) push-deps: - docker push ${GOOGLE_GCR_HOSTNAME}/${GOOGLE_PROJECT_ID}/deps:latest + $(SOURCE_ENV) && docker push ${GOOGLE_GCR_HOSTNAME}/${GOOGLE_PROJECT_ID}/deps:latest push: - docker-compose -f ${BUILD_DEPLOY_DIR}/docker-compose.yml -f ${BUILD_DEPLOY_DIR}/docker-compose.build.yml push ${service} + $(SOURCE_ENV) && docker-compose -f ${BUILD_DEPLOY_DIR}/docker-compose.yml -f ${BUILD_DEPLOY_DIR}/docker-compose.build.yml push ${service} pull: docker-compose pull ${service} build-deps: - sh ./scripts/build-deps.sh ${GOOGLE_GCR_HOSTNAME} ${GOOGLE_PROJECT_ID} ${BUILD_PACKAGE_DIR} ${PWD} + $(SOURCE_ENV) && sh ./scripts/build-deps.sh ${GOOGLE_GCR_HOSTNAME} ${GOOGLE_PROJECT_ID} ${BUILD_PACKAGE_DIR} ${PWD} start: sh ./scripts/start.sh ${BUILD_DEPLOY_DIR} ${env} ${service} build: build-deps - sh ./scripts/build.sh ${PWD} ${SERVICE_DOCKERFILE} ${BUILD_DEPLOY_DIR} ${service} + $(SOURCE_ENV) && sh ./scripts/build.sh ${PWD} ${SERVICE_DOCKERFILE} ${BUILD_DEPLOY_DIR} ${service} stop: docker-compose ${BUILD_DEPLOY_DIR}/docker-compose.yml down ${service} diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index b275c99..a8760ad 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -79,11 +79,10 @@ func GetUserProfiles(users ...User) ([]Profile, error) { for i, u := range users { p := Profile{UID: u.ID} userRecord, err := client.GetUser(ctx, u.ID) - if err != nil { - return []Profile{}, fmt.Errorf("error getting user '%v': %v", u.ID, err) + if err == nil { + p.Name = userRecord.DisplayName + p.ProfilePicture = userRecord.PhotoURL } - p.Name = userRecord.DisplayName - p.ProfilePicture = userRecord.PhotoURL profiles[i] = p } return profiles, nil diff --git a/pkg/services/circle/handlers/handlers.go b/pkg/services/circle/handlers/handlers.go index e7d8877..9acc6dd 100644 --- a/pkg/services/circle/handlers/handlers.go +++ b/pkg/services/circle/handlers/handlers.go @@ -27,13 +27,13 @@ func AddToCircle(s *common.Service) http.Handler { // Read the request body body, err := ioutil.ReadAll(r.Body) if err != nil { - common.ThrowError(w, fmt.Errorf("Error reading request body: %v", err.Error()), http.StatusInternalServerError) + common.ThrowError(w, fmt.Errorf("error reading request body: %v", err.Error()), http.StatusBadRequest) return } // Unmarshal the circle var circle models.Circle if err := json.Unmarshal(body, &circle); err != nil { - common.ThrowError(w, fmt.Errorf("Error unmarshalling circle: %v", err.Error()), http.StatusInternalServerError) + common.ThrowError(w, fmt.Errorf("Error unmarshalling circle: %v", err.Error()), http.StatusBadRequest) return } @@ -42,6 +42,19 @@ func AddToCircle(s *common.Service) http.Handler { return } + // Check if circle exists + query := s.DB.Find(&circle) + if query.RecordNotFound() { + // doesn't exist + common.ThrowError(w, fmt.Errorf("bad request: circle %v doesn't exist", circle.ID), http.StatusBadRequest) + return + } + + if query.Error != nil { + common.ThrowError(w, query.Error, http.StatusInternalServerError) + return + } + err = models.AddUserToCircle(s, &user, &circle, true) if err != nil { common.ThrowError(w, fmt.Errorf("error adding user to circle: %v", err.Error()), http.StatusInternalServerError) diff --git a/pkg/services/circle/handlers/handlers_test.go b/pkg/services/circle/handlers/handlers_test.go index ff39845..01a6804 100644 --- a/pkg/services/circle/handlers/handlers_test.go +++ b/pkg/services/circle/handlers/handlers_test.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/json" - "io/ioutil" "net/http" "net/http/httptest" "os" @@ -16,122 +15,94 @@ import ( "github.com/safe-distance/socium-infra/pkg/auth" "github.com/safe-distance/socium-infra/pkg/common" "github.com/safe-distance/socium-infra/pkg/services/circle/models" + "github.com/stretchr/testify/assert" ) var s *common.Service var saramaConfig *sarama.Config +var producer sarama.AsyncProducer +var validUsers = []auth.User{ + {ID: "1"}, + {ID: "2"}, + {ID: "3"}, +} + +var validCircle = models.Circle{ID: "123", Users: validUsers} +var testUID = "TEST_UID" +var testToken = &auth.Token{UID: testUID} func TestMain(m *testing.M) { os.Setenv("DB_PROVIDER", "sqlite3") os.Setenv("DB_CONNECTION_STRING", ":memory:") saramaConfig = sarama.NewConfig() saramaConfig.Producer.Return.Successes = true - producer := common.NewNullAsyncProducer() - s = common.NewService(config.ServiceName, config.ServicePathPrefix, models.Schema, producer, config.ProductionTopic) + producer = common.NewNullAsyncProducer() os.Exit(m.Run()) } -func TestUserHandler(t *testing.T) { - // Create a test user and a test token - testUID := "TEST_UID" - testCircleID := "TEST_CIRCLE_ID" - testCircle := models.Circle{ID: testCircleID} - testToken := &auth.Token{UID: testUID} - - // Marshal the text interaction to JSON, as it would be received in a POST request - payload, err := json.Marshal(testCircle) - if err != nil { - t.Fatalf("Error marshaling test circle: %v", err.Error()) - } - - // Create a test request and add the test token to its context - r := httptest.NewRequest("PATCH", "/circles/add", bytes.NewBuffer(payload)) - ctx := auth.AddTokenTo(context.Background(), testToken) +func TestGetCircle_Valid(t *testing.T) { + s = common.NewService(config.ServiceName, config.ServicePathPrefix, models.Schema, producer, config.ProductionTopic) + err := s.DB.Create(&validCircle).Error + assert.Nil(t, err) + token := &auth.Token{UID: "1"} + r := httptest.NewRequest("GET", "/irrelevant", nil) + ctx := auth.AddTokenTo(context.Background(), token) w := httptest.NewRecorder() - // Call the interaction handler with the response recorder and test request - AddToCircle(s).ServeHTTP(w, r.WithContext(ctx)) - - if w.Code != http.StatusOK { - body, _ := ioutil.ReadAll(w.Result().Body) - t.Fatalf("Error adding user to circle: %v", string(body)) - } - - // Read the body of the response recorder - resBuffer := bytes.NewBuffer([]byte{}) - _, err = resBuffer.ReadFrom(w.Result().Body) - if err != nil { - t.Fatalf("Error reading from response buffer: %v", err.Error()) - } - - // Unmarshal the returned interaction - var createdCircle models.Circle - err = json.Unmarshal(resBuffer.Bytes(), &createdCircle) - if err != nil { - t.Fatalf("Error unmarshalling response body into Circle: %v", err.Error()) - } - - t.Logf("PATCH response circle: %+v", createdCircle) - - // PATCH - newUserID := "NEW_USER_ID" - newTestToken := &auth.Token{UID: newUserID} - - // Marshal the text interaction to JSON, as it would be received in a PATCH request - payload, err = json.Marshal(testCircle) - if err != nil { - t.Fatalf("Error marshaling test circle: %v", err.Error()) - } - - // Make a PATCH request to update the user - r = httptest.NewRequest("PATCH", "/circles", bytes.NewBuffer(payload)) - w = httptest.NewRecorder() - ctx = auth.AddTokenTo(context.Background(), newTestToken) - - AddToCircle(s).ServeHTTP(w, r.WithContext(ctx)) - - // Read the body of the response recorder - resBuffer = bytes.NewBuffer([]byte{}) - _, err = resBuffer.ReadFrom(w.Result().Body) - if err != nil { - t.Fatalf("Error reading from response buffer: %v", err.Error()) - } - - // Unmarshal the returned interaction - var updatedCircle models.Circle - err = json.Unmarshal(resBuffer.Bytes(), &updatedCircle) - if err != nil { - t.Fatalf("Error unmarshalling response body into Circle: %v", err.Error()) - } - - t.Logf("PATCH response circle: %+v", updatedCircle) - - // Make a GET request to retrieve the interaction - r = httptest.NewRequest("GET", "/circles", nil) - w = httptest.NewRecorder() - ctx = auth.AddTokenTo(r.Context(), testToken) - // Call the interaction handler with the response recorder and test request GetCircle(s).ServeHTTP(w, r.WithContext(ctx)) + assert.Equal(t, http.StatusOK, w.Code) + var responseCircle models.CircleResponse + err = json.Unmarshal(w.Body.Bytes(), &responseCircle) + assert.Nil(t, err) + assert.Equal(t, validCircle.ID, responseCircle.ID) + assert.Equal(t, len(validCircle.Users), len(responseCircle.Users)) - // Read the body of the response recorder - resBuffer = bytes.NewBuffer([]byte{}) - _, err = resBuffer.ReadFrom(w.Result().Body) - if err != nil { - t.Fatalf("Error reading from response buffer: %v", err.Error()) - } - - // Unmarshal the returned interaction - var retrievedCircle models.Circle - err = json.Unmarshal(resBuffer.Bytes(), &retrievedCircle) - if err != nil { - t.Fatalf("Error unmarshalling response body into Circle: %v", err.Error()) - } +} - t.Logf("GET response circle: %+v", retrievedCircle) +func TestGetCircle_NoCircle(t *testing.T) { + s = common.NewService(config.ServiceName, config.ServicePathPrefix, models.Schema, producer, config.ProductionTopic) + err := s.DB.Create(&validCircle).Error + assert.Nil(t, err) + token := &auth.Token{UID: "99"} + r := httptest.NewRequest("GET", "/irrelevant", nil) + ctx := auth.AddTokenTo(context.Background(), token) + w := httptest.NewRecorder() + GetCircle(s).ServeHTTP(w, r.WithContext(ctx)) + assert.Equal(t, http.StatusOK, w.Code) + var responseCircle models.CircleResponse + err = json.Unmarshal(w.Body.Bytes(), &responseCircle) + assert.Nil(t, err) + assert.Equal(t, 1, len(responseCircle.Users)) + assert.Equal(t, token.UID, responseCircle.Users[0].UID) +} - // Check that the two interactions are equal - if len(updatedCircle.Users) != len(retrievedCircle.Users) { - t.Fatal("Fail: circles returned by PATCH and by GET are not equal") - } +func TestAddToCircle_Valid(t *testing.T) { + s = common.NewService(config.ServiceName, config.ServicePathPrefix, models.Schema, producer, config.ProductionTopic) + err := s.DB.Create(&validCircle).Error + assert.Nil(t, err) + token := &auth.Token{UID: "4"} + payload, err := json.Marshal(map[string]string{"id": "123"}) + assert.Nil(t, err) + r := httptest.NewRequest("PATCH", "/irrelevant", bytes.NewBuffer(payload)) + ctx := auth.AddTokenTo(context.Background(), token) + w := httptest.NewRecorder() + AddToCircle(s).ServeHTTP(w, r.WithContext(ctx)) + assert.Equal(t, http.StatusOK, w.Code) + var responseCircle models.CircleResponse + err = json.Unmarshal(w.Body.Bytes(), &responseCircle) + assert.Equal(t, 4, len(responseCircle.Users)) +} +func TestAddToCircle_Invalid(t *testing.T) { + s = common.NewService(config.ServiceName, config.ServicePathPrefix, models.Schema, producer, config.ProductionTopic) + err := s.DB.Create(&validCircle).Error + assert.Nil(t, err) + token := &auth.Token{UID: "4"} + payload, err := json.Marshal(map[string]string{"id": "9999"}) + assert.Nil(t, err) + r := httptest.NewRequest("PATCH", "/irrelevant", bytes.NewBuffer(payload)) + ctx := auth.AddTokenTo(context.Background(), token) + w := httptest.NewRecorder() + AddToCircle(s).ServeHTTP(w, r.WithContext(ctx)) + assert.Equal(t, http.StatusBadRequest, w.Code) } diff --git a/pkg/services/circle/models/models.go b/pkg/services/circle/models/models.go index fe02074..384c36c 100644 --- a/pkg/services/circle/models/models.go +++ b/pkg/services/circle/models/models.go @@ -46,7 +46,6 @@ func AddUserToCircle(s *common.Service, user *auth.User, circle *Circle, mergeCi if err := s.DB.Preload("Users").FirstOrCreate(circle, Circle{ID: circle.ID}).Error; err != nil { return fmt.Errorf("error retrieving/creating circle: %v", err.Error()) } - fmt.Printf("circle: %+v\n", circle) oldCircleID := user.CircleID // Start association mode association := s.DB.Model(circle).Association("Users") diff --git a/scripts/deploy b/scripts/deploy deleted file mode 100755 index f5342e4..0000000 --- a/scripts/deploy +++ /dev/null @@ -1,3 +0,0 @@ -gcloud beta compute scp --zone "us-central1-a" --project "spatiumsocialis" ./docker-compose.yml ./docker-compose.prod.yml Makefile spatium-prod:~/ -gcloud beta compute ssh 'spatium-prod' --zone "us-central1-a" --project "spatiumsocialis" --command 'make pull; make start env=prod; exit' - diff --git a/scripts/deploy.sh b/scripts/deploy.sh index f5342e4..977c58d 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,3 +1,3 @@ -gcloud beta compute scp --zone "us-central1-a" --project "spatiumsocialis" ./docker-compose.yml ./docker-compose.prod.yml Makefile spatium-prod:~/ +gcloud beta compute scp --zone "us-central1-a" --project "spatiumsocialis" ./build Makefile spatium-prod:~/ gcloud beta compute ssh 'spatium-prod' --zone "us-central1-a" --project "spatiumsocialis" --command 'make pull; make start env=prod; exit'