diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..3dbc680 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,21 @@ +name: Tests + +on: + push: + branches: + - '**' + + pull_request: + branches: + - '**' + +jobs: + run_tests: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Run script file + run: | + chmod +x ./scripts/test.sh + ./scripts/test.sh + shell: bash \ No newline at end of file diff --git a/.gitignore b/.gitignore index 25acef6..d279bd3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ # Ignore build folder .aws-sam/ -env.json \ No newline at end of file +env.json +*/firestore-debug.log \ No newline at end of file diff --git a/health-check/go.mod b/health-check/go.mod index 9057865..b79cb69 100644 --- a/health-check/go.mod +++ b/health-check/go.mod @@ -1,4 +1,7 @@ -require github.com/aws/aws-lambda-go v1.28.0 +require ( + github.com/aws/aws-lambda-go v1.28.0 + github.com/stretchr/testify v1.6.1 +) module health diff --git a/health-check/go.sum b/health-check/go.sum index 29c9194..2c6fdd8 100644 --- a/health-check/go.sum +++ b/health-check/go.sum @@ -1,6 +1,4 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/aws/aws-lambda-go v1.23.0 h1:Vjwow5COkFJp7GePkk9kjAo/DyX36b7wVPKwseQZbRo= -github.com/aws/aws-lambda-go v1.23.0/go.mod h1:jJmlefzPfGnckuHdXX7/80O3BvUUi12XOkbv4w9SGLU= github.com/aws/aws-lambda-go v1.28.0 h1:fZiik1PZqW2IyAN4rj+Y0UBaO1IDFlsNo9Zz/XnArK4= github.com/aws/aws-lambda-go v1.28.0/go.mod h1:jJmlefzPfGnckuHdXX7/80O3BvUUi12XOkbv4w9SGLU= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= @@ -16,6 +14,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/health-check/main_test.go b/health-check/main_test.go new file mode 100644 index 0000000..981ae90 --- /dev/null +++ b/health-check/main_test.go @@ -0,0 +1,30 @@ +package main + +import ( + "testing" + + "github.com/aws/aws-lambda-go/events" + "github.com/stretchr/testify/assert" +) + +func TestHandler(t *testing.T) { + + tests := []struct { + request events.APIGatewayProxyRequest + expect string + err error + }{ + { + request: events.APIGatewayProxyRequest{}, + expect: "Awesome, Server health is good!!!", + err: nil, + }, + } + + for _, test := range tests { + response, err := handler(test.request) + assert.IsType(t, test.err, err) + assert.Equal(t, test.expect, response.Body) + } + +} diff --git a/profile/firebase.json b/profile/firebase.json new file mode 100644 index 0000000..25eb629 --- /dev/null +++ b/profile/firebase.json @@ -0,0 +1,11 @@ +{ + "emulators": { + "firestore": { + "port": 8090 + }, + "ui": { + "enabled": true, + "port": 4000 + } + } +} diff --git a/profile/go.mod b/profile/go.mod index 6022694..0076f9b 100644 --- a/profile/go.mod +++ b/profile/go.mod @@ -6,12 +6,15 @@ require ( github.com/aws/aws-sdk-go v1.42.51 github.com/go-ozzo/ozzo-validation/v4 v4.3.0 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - golang.org/x/crypto v0.1.0 + github.com/stretchr/testify v1.7.0 + golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 + golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect + golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect google.golang.org/api v0.68.0 google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) -module health +module profile go 1.16 diff --git a/profile/go.sum b/profile/go.sum index 2a98178..39fb30a 100644 --- a/profile/go.sum +++ b/profile/go.sum @@ -192,8 +192,10 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -626,6 +628,7 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/profile/main.go b/profile/main.go index 6f53d23..ecdd0c3 100644 --- a/profile/main.go +++ b/profile/main.go @@ -39,6 +39,11 @@ type Log struct { Body map[string]interface{} `firestore:"body,omitempty"` } +type deps struct { + client *firestore.Client + ctx context.Context +} + type Res struct { FirstName string `json:"first_name"` LastName string `json:"last_name"` @@ -533,12 +538,10 @@ func callProfileService(client *firestore.Client, ctx context.Context, doc *fire /* Controller */ -func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - ctx := context.Background() - client, err := initializeFirestoreClient(ctx) - if err != nil { - return events.APIGatewayProxyResponse{}, err - } +func (d *deps) handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + + ctx := d.ctx + client := d.client totalProfilesChecked := 0 profilesSkipped := structProfilesSkipped{} @@ -633,5 +636,16 @@ func getReport(totalProfilesChecked int, profileDiffsStored []string, profilesSk } func main() { - lambda.Start(handler) + ctx := context.Background() + client, err := initializeFirestoreClient(ctx) + if err != nil { + return + } + + d := deps{ + client: client, + ctx: ctx, + } + + lambda.Start(d.handler) } diff --git a/profile/main_test.go b/profile/main_test.go new file mode 100644 index 0000000..22b2a42 --- /dev/null +++ b/profile/main_test.go @@ -0,0 +1,53 @@ +package main + +import ( + "context" + "os" + "testing" + + "cloud.google.com/go/firestore" + "github.com/aws/aws-lambda-go/events" + "github.com/stretchr/testify/assert" +) + +func TestHandler(t *testing.T) { + + os.Setenv("environment", "test") + defer os.Unsetenv("environment") + + tests := []struct { + request events.APIGatewayProxyRequest + expect string + err error + }{ + // Format + // { + // request: events.APIGatewayProxyRequest{}, + // expect: "Verification Process Done", + // err: nil, + // }, + } + + ctx := context.Background() + client, err := firestore.NewClient(ctx, "test") + if err != nil { + return + } + + client.Collection("users").Doc("ACD").Set(ctx, map[string]interface{}{ + "chaincode": "ABCD", + "profileURL": "https://identity.dev", + }) + + d := deps{ + client: client, + ctx: ctx, + } + + for _, test := range tests { + response, err := d.handler(test.request) + assert.IsType(t, test.err, err) + assert.Equal(t, test.expect, response) + } + +} diff --git a/scripts/dev.sh b/scripts/dev.sh index f20d9a2..44a60a8 100644 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -1,2 +1,9 @@ +cd health-check +go mod tidy +cd ../verify +go mod tidy +cd ../profile +go mod tidy +cd .. sam.cmd build sam.cmd local start-api --env-vars env.json \ No newline at end of file diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100644 index 0000000..91275c3 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,21 @@ +cd health-check +go mod tidy +go test -v +npx kill-port 8090 +cd ../verify +go mod tidy +npm install -g firebase-tools +if (firebase --project="test" emulators:exec "go test"); then + echo "Exited Success" +else + exit 1 +fi +npx kill-port 8090 +cd ../profile +go mod tidy +if (firebase --project="test" emulators:exec "go test"); then + echo "Exited Success" +else + exit 1 +fi +npx kill-port 8090 \ No newline at end of file diff --git a/verify/firebase.json b/verify/firebase.json new file mode 100644 index 0000000..25eb629 --- /dev/null +++ b/verify/firebase.json @@ -0,0 +1,11 @@ +{ + "emulators": { + "firestore": { + "port": 8090 + }, + "ui": { + "enabled": true, + "port": 4000 + } + } +} diff --git a/verify/go.mod b/verify/go.mod index 5b06b90..c401113 100644 --- a/verify/go.mod +++ b/verify/go.mod @@ -1,4 +1,4 @@ -module main +module verify go 1.17 @@ -7,7 +7,8 @@ require ( firebase.google.com/go v3.13.0+incompatible github.com/aws/aws-lambda-go v1.28.0 github.com/aws/aws-sdk-go v1.42.51 - golang.org/x/crypto v0.1.0 + github.com/stretchr/testify v1.7.0 + golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2 google.golang.org/api v0.68.0 ) @@ -16,11 +17,13 @@ require ( cloud.google.com/go/compute v1.2.0 // indirect cloud.google.com/go/iam v0.1.1 // indirect cloud.google.com/go/storage v1.20.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.7 // indirect github.com/googleapis/gax-go/v2 v2.1.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect go.opencensus.io v0.23.0 // indirect golang.org/x/net v0.7.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect @@ -31,4 +34,5 @@ require ( google.golang.org/genproto v0.0.0-20220210181026-6fee9acbd336 // indirect google.golang.org/grpc v1.44.0 // indirect google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect ) diff --git a/verify/go.sum b/verify/go.sum index c8dfd58..4cac40e 100644 --- a/verify/go.sum +++ b/verify/go.sum @@ -188,8 +188,10 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -227,9 +229,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2 h1:XdAboW3BNMv9ocSCOk/u1MFioZGzCNkiJZ19v9Oe3Ig= +golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -301,6 +302,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= @@ -395,8 +397,6 @@ golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -626,6 +626,7 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/verify/helpers_test.go b/verify/helpers_test.go new file mode 100644 index 0000000..f5313f3 --- /dev/null +++ b/verify/helpers_test.go @@ -0,0 +1,217 @@ +package main + +import ( + "context" + "errors" + "net/http" + "net/http/httptest" + "os" + "testing" + "time" + + "cloud.google.com/go/firestore" + "github.com/stretchr/testify/assert" +) + +var ( + ctx context.Context + cancel context.CancelFunc + client *firestore.Client +) + +func TestMain(m *testing.M) { + ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) + client = newFirestoreMockClient(ctx) + + code := m.Run() + + cancel() + client.Close() + + os.Exit(code) +} + +func TestSetProfileStatus(t *testing.T) { + // When status is BLOCKED, expect to set chaincode to empty string + ID := "1234" + profileStatus := "BLOCKED" + err := setProfileStatus(client, ctx, ID, profileStatus) + if err != nil { + t.Errorf("setProfileStatus returned an error: %v", err) + } + + userDoc, err := client.Collection("users").Doc(ID).Get(ctx) + if err != nil { + t.Errorf("Unable to fetch user document: %v", err) + } + + assert.Equal(t, "", userDoc.Data()["chaincode"]) + assert.Equal(t, profileStatus, userDoc.Data()["profileStatus"]) + + // if status is VERIFIED / PENDING, expect to set profile status without errors + ID = "abcd" + profileStatus = "VERIFIED" + err = setProfileStatus(client, ctx, ID, profileStatus) + if err != nil { + t.Errorf("setProfileStatus returned an error: %v", err) + } + + userDoc, err = client.Collection("users").Doc(ID).Get(ctx) + if err != nil { + t.Errorf("Unable to fetch user document: %v", err) + } + + assert.Equal(t, profileStatus, userDoc.Data()["profileStatus"]) +} + +func TestGetUserData(t *testing.T) { + testCases := []struct { + name string + userId string + profileURL interface{} + profileStatus interface{} + chaincode interface{} + expectedErr error + }{ + { + name: "success", + userId: "1", + profileURL: "http://test.com", + profileStatus: "VERIFIED", + chaincode: "abc123", + expectedErr: nil, + }, + { + name: "missing profile url", + userId: "2", + profileURL: nil, + profileStatus: "BLOCKED", + chaincode: "abc123", + expectedErr: errors.New("profile url is not a string"), + }, + { + name: "missing chaincode", + userId: "3", + profileURL: "http://test.com", + profileStatus: "BLOCKED", + chaincode: "", + expectedErr: errors.New("chaincode is blocked"), + }, + { + name: "invalid chaincode", + userId: "4", + profileURL: "http://test.com", + profileStatus: "BLOCKED", + chaincode: 123, + expectedErr: errors.New("chaincode is not a string"), + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + client.Collection("users").Doc(testCase.userId).Set(ctx, map[string]interface{}{ + "chaincode": testCase.chaincode, + "profileURL": testCase.profileURL, + "profileStatus": testCase.profileStatus, + }) + + profileURL, profileStatus, chaincode, err := getUserData(client, ctx, testCase.userId) + + assert.Equal(t, testCase.expectedErr, err) + if testCase.expectedErr == nil { + assert.Equal(t, testCase.profileURL.(string), profileURL) + assert.Equal(t, testCase.profileStatus.(string), profileStatus) + assert.Equal(t, testCase.chaincode.(string), chaincode) + } + }) + } +} + +func TestGetUserIdFromBody(t *testing.T) { + // valid request body + body := []byte(`{"userId": "123"}`) + expected := "123" + + actual := getUserIdFromBody(body) + + if actual != expected { + t.Errorf("getUserIdFromBody returned %v, expected %v", actual, expected) + } + + // empty request body + body = []byte(``) + expected = "" + + actual = getUserIdFromBody(body) + + if actual != expected { + t.Errorf("getUserIdFromBody returned %v, expected %v", actual, expected) + } + + // invalid request body + body = []byte(`{"invalidProperty": ""}`) + expected = "" + + actual = getUserIdFromBody(body) + + if actual != expected { + t.Errorf("getUserIdFromBody returned %v, expected %v", actual, expected) + } +} + +type testVerifyData struct { + name string + path string + chaincode string + mockStatusCode int + mockResBody string + expectedStatus string + expectedErr error +} + +func TestVerify(t *testing.T) { + testCases := []testVerifyData{ + { + name: "VERIFIED", + path: "/profile-one", + chaincode: "testchaincode", + mockStatusCode: http.StatusOK, + mockResBody: `{"hash": "$2a$12$ScGc2Q0t0rqqSJK1E2W/WuaRVAchaVWdUqb1hQi21cFTnOVvlIdry"}`, + expectedStatus: "VERIFIED", + expectedErr: nil, + }, + { + name: "BLOCKED", + path: "/profile-two", + chaincode: "invalid", + mockStatusCode: http.StatusForbidden, + mockResBody: `{"hash": "abcdefghijklmnopqrstuvwxyz"}`, + expectedStatus: "BLOCKED", + expectedErr: nil, + }, + } + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/profile-one" { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"hash": "$2a$12$ScGc2Q0t0rqqSJK1E2W/WuaRVAchaVWdUqb1hQi21cFTnOVvlIdry"}`)) + } + + if r.URL.Path == "/profile-two" { + w.WriteHeader(http.StatusForbidden) + w.Write([]byte(`{"hash": "abcdefghijklmnopqrstuvwxyz"}`)) + } + })) + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + + status, err := verify(server.URL+testCase.path, testCase.chaincode) + + assert.Equal(t, testCase.expectedStatus, status) + assert.Equal(t, testCase.expectedErr, err) + }) + } + + defer server.Close() +} diff --git a/verify/main.go b/verify/main.go index d31bc5a..ae3fb44 100644 --- a/verify/main.go +++ b/verify/main.go @@ -24,7 +24,7 @@ import ( ) /* - Structures +Structures */ type Log struct { Type string `firestore:"type,omitempty"` @@ -33,6 +33,11 @@ type Log struct { Body map[string]interface{} `firestore:"body,omitempty"` } +type deps struct { + client *firestore.Client + ctx context.Context +} + /* Util */ @@ -65,10 +70,14 @@ func getFirestoreKey() string { } /* - Function to initialize the firestore client +Function to initialize the firestore client */ func initializeFirestoreClient(ctx context.Context) (*firestore.Client, error) { - sa := option.WithCredentialsJSON([]byte(getFirestoreKey())) + var firestoreKey = getFirestoreKey() + if firestoreKey == "" { + return nil, errors.New("no firestore key found") + } + sa := option.WithCredentialsJSON([]byte(firestoreKey)) app, err := firebase.NewApp(ctx, nil, sa) if err != nil { return nil, err @@ -114,7 +123,7 @@ func logVerification(client *firestore.Client, ctx context.Context, status strin } /* - Function for setting the profileStatus in user object in firestore +Function for setting the profileStatus in user object in firestore */ func setProfileStatus(client *firestore.Client, ctx context.Context, id string, status string) error { var newData = map[string]interface{}{ @@ -138,7 +147,7 @@ func setProfileStatus(client *firestore.Client, ctx context.Context, id string, } /* - Function to get the userData using userId +Function to get the userData using userId */ func getUserData(client *firestore.Client, ctx context.Context, userId string) (string, string, string, error) { dsnap, err := client.Collection("users").Doc(userId).Get(ctx) @@ -185,7 +194,7 @@ func getUserData(client *firestore.Client, ctx context.Context, userId string) ( } /* - Function to extract userId from the request body +Function to extract userId from the request body */ func getUserIdFromBody(body []byte) string { type extractedBody struct { @@ -198,7 +207,7 @@ func getUserIdFromBody(body []byte) string { } /* - Function to generate random string +Function to generate random string */ func randSalt(n int) string { var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789") @@ -231,7 +240,13 @@ func verify(profileURL string, chaincode string) (string, error) { reqBody := bytes.NewBuffer(postBody) - resp, err := http.Post(profileURL, "application/json", reqBody) + req, err := http.NewRequest("POST", profileURL, reqBody) + if err != nil { + return "", err + } + + client := &http.Client{} + resp, err := client.Do(req) if err != nil { return "", err } @@ -252,21 +267,16 @@ func verify(profileURL string, chaincode string) (string, error) { } /* - Main Handler Function +Main Handler Function */ -func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - ctx := context.Background() - client, err := initializeFirestoreClient(ctx) - if err != nil { - return events.APIGatewayProxyResponse{}, err - } +func (d *deps) handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { var userId string = getUserIdFromBody([]byte(request.Body)) if userId == "" { return events.APIGatewayProxyResponse{}, errors.New("no userId provided") } - profileURL, profileStatus, chaincode, err := getUserData(client, ctx, userId) + profileURL, profileStatus, chaincode, err := getUserData(d.client, d.ctx, userId) if err != nil { return events.APIGatewayProxyResponse{}, err } @@ -287,9 +297,9 @@ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyRespo if err != nil { return events.APIGatewayProxyResponse{}, err } - logVerification(client, ctx, status, profileURL, userId) - setProfileStatus(client, ctx, userId, status) - defer client.Close() + logVerification(d.client, d.ctx, status, profileURL, userId) + setProfileStatus(d.client, d.ctx, userId, status) + defer d.client.Close() return events.APIGatewayProxyResponse{ Body: "Verification Process Done", @@ -298,8 +308,19 @@ func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyRespo } /* - Starts the lambda (Entry Point) +Starts the lambda (Entry Point) */ func main() { - lambda.Start(handler) + ctx := context.Background() + client, err := initializeFirestoreClient(ctx) + if err != nil { + return + } + + d := deps{ + client: client, + ctx: ctx, + } + + lambda.Start(d.handler) } diff --git a/verify/main_test.go b/verify/main_test.go new file mode 100644 index 0000000..cae6106 --- /dev/null +++ b/verify/main_test.go @@ -0,0 +1,111 @@ +package main + +import ( + "context" + "errors" + "fmt" + "log" + "net/http" + "net/http/httptest" + "os" + "testing" + "time" + + "cloud.google.com/go/firestore" + "github.com/aws/aws-lambda-go/events" + "github.com/stretchr/testify/assert" +) + +func newFirestoreMockClient(ctx context.Context) *firestore.Client { + client, err := firestore.NewClient(ctx, "test") + if err != nil { + log.Fatalf("firebase.NewClient err: %v", err) + } + + return client +} + +func addUsers(ctx context.Context, client *firestore.Client, users []map[string]interface{}) error { + for _, user := range users { + id, ok := user["userId"].(string) + if !ok { + return fmt.Errorf("userId is missing or not a string: %v", user) + } + + delete(user, "userId") + if _, err := client.Collection("users").Doc(id).Set(ctx, user); err != nil { + return fmt.Errorf("cannot add user %s: %v", id, err) + } + + } + + return nil +} + +func TestHandler(t *testing.T) { + os.Setenv("environment", "test") + defer os.Unsetenv("environment") + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + client := newFirestoreMockClient(ctx) + defer cancel() + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/profile-two/verification" { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"hash": "$2a$12$ScGc2Q0t0rqqSJK1E2W/WuaRVAchaVWdUqb1hQi21cFTnOVvlIdry"}`)) + } + })) + defer server.Close() + + verifiedUserId := "123" + unverifiedUserId := "321" + users := []map[string]interface{}{ + {"userId": verifiedUserId, "chaincode": "abcdefgh", "profileURL": server.URL + "/profile-one", "profileStatus": "VERIFIED"}, + {"userId": unverifiedUserId, "chaincode": "testchaincode", "profileURL": server.URL + "/profile-two", "profileStatus": "BLOCKED"}, + } + + if err := addUsers(ctx, client, users); err != nil { + t.Fatalf("failed to add users: %v", err) + } + + testCases := []struct { + name string + request events.APIGatewayProxyRequest + expect string + err error + }{ + { + name: "verified user", + request: events.APIGatewayProxyRequest{Body: fmt.Sprintf(`{ "userId": "%s" }`, verifiedUserId)}, + expect: "Already Verified", + err: nil, + }, + { + name: "unverified user", + request: events.APIGatewayProxyRequest{Body: fmt.Sprintf(`{ "userId": "%s" }`, unverifiedUserId)}, + expect: "Verification Process Done", + err: nil, + }, + { + name: "no userId", + request: events.APIGatewayProxyRequest{Body: `{}`}, + expect: "", + err: errors.New("no userId provided"), + }, + } + + d := deps{ + client: client, + ctx: ctx, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + response, err := d.handler(testCase.request) + assert.Equal(t, testCase.err, err) + assert.Equal(t, testCase.expect, response.Body) + }) + } + +}