From 8c77331c40ada5f021297be53ef5957022a8dbb7 Mon Sep 17 00:00:00 2001 From: Nahshon Unna-Tsameret Date: Tue, 19 Apr 2022 13:17:22 +0300 Subject: [PATCH] Move to ginkgo v2 Signed-off-by: Nahshon Unna-Tsameret --- Makefile | 3 +- common/datatypes_test.go | 2 +- controller/controller_test.go | 2 +- go.mod | 16 +- go.sum | 45 ++- hat/hat_test.go | 2 +- notifier/notifier_test.go | 2 +- state/change_test.go | 2 +- state/state_suite_test.go | 2 +- state/state_test.go | 2 +- webapp/websocket.go | 404 ++++++++++---------- webapp/websocket_test.go | 685 +++++++++++++++++----------------- 12 files changed, 591 insertions(+), 576 deletions(-) diff --git a/Makefile b/Makefile index 5ecb6df..57ea2ee 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,8 @@ build-backend: test build: build-ui build-backend test: - go test ./... + go install github.com/onsi/ginkgo/v2/ginkgo@latest + ginkgo -r . .PHONY: build \ test \ diff --git a/common/datatypes_test.go b/common/datatypes_test.go index 0a2ad83..3cda9b5 100644 --- a/common/datatypes_test.go +++ b/common/datatypes_test.go @@ -5,7 +5,7 @@ import ( "encoding/json" "testing" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) diff --git a/controller/controller_test.go b/controller/controller_test.go index 73c7e92..9111d9b 100644 --- a/controller/controller_test.go +++ b/controller/controller_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/nunnatsa/piHatDraw/common" diff --git a/go.mod b/go.mod index 4ef2fa5..562577e 100644 --- a/go.mod +++ b/go.mod @@ -5,17 +5,13 @@ go 1.18 require ( github.com/gorilla/websocket v1.4.2 github.com/nathany/bobblehat v0.0.0-20170421151738-14b0d1b4643e - github.com/onsi/ginkgo v1.16.5 - github.com/onsi/gomega v1.10.1 + github.com/onsi/ginkgo/v2 v2.1.3 + github.com/onsi/gomega v1.19.0 ) require ( - github.com/fsnotify/fsnotify v1.4.9 // indirect - github.com/nxadm/tail v1.4.8 // indirect - golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect - golang.org/x/sys v0.0.0-20210112080510-489259a85091 // indirect - golang.org/x/text v0.3.3 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v2 v2.3.0 // indirect + golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 // indirect + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect + golang.org/x/text v0.3.7 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 796172e..7e1e166 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,9 @@ +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -10,26 +12,33 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/nathany/bobblehat v0.0.0-20170421151738-14b0d1b4643e h1:laO0ulsXfuedAk+m0frxy+o7uaoclS9SsVTBCSl6bew= github.com/nathany/bobblehat v0.0.0-20170421151738-14b0d1b4643e/go.mod h1:rxS+MnnPI6N0Ia8dxXUxQBrpvCXvOsGF3kAsnNMlF4Y= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -42,8 +51,10 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2 h1:6mzvA99KwZxbOrxww4EvWVQUnN1+xEu9tafK5ZxkYeA= +golang.org/x/net v0.0.0-20220418201149-a630d4f3e7a2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -53,34 +64,42 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/hat/hat_test.go b/hat/hat_test.go index 39ad4a9..9aa31a1 100644 --- a/hat/hat_test.go +++ b/hat/hat_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) diff --git a/notifier/notifier_test.go b/notifier/notifier_test.go index 3be5798..91956f4 100644 --- a/notifier/notifier_test.go +++ b/notifier/notifier_test.go @@ -5,7 +5,7 @@ import ( "sync" "testing" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) diff --git a/state/change_test.go b/state/change_test.go index bc17bc2..7a6449f 100644 --- a/state/change_test.go +++ b/state/change_test.go @@ -1,7 +1,7 @@ package state import ( - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) diff --git a/state/state_suite_test.go b/state/state_suite_test.go index ec33c3b..2760154 100644 --- a/state/state_suite_test.go +++ b/state/state_suite_test.go @@ -3,7 +3,7 @@ package state import ( "testing" - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) diff --git a/state/state_test.go b/state/state_test.go index d259c2b..b246123 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -1,7 +1,7 @@ package state import ( - . "github.com/onsi/ginkgo" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/nunnatsa/piHatDraw/common" diff --git a/webapp/websocket.go b/webapp/websocket.go index 1339018..eced6e1 100644 --- a/webapp/websocket.go +++ b/webapp/websocket.go @@ -1,29 +1,29 @@ package webapp import ( - "encoding/json" - "fmt" - "image" - "image/color" - "image/png" - "log" - "net/http" - "strconv" - - "github.com/gorilla/websocket" - - "github.com/nunnatsa/piHatDraw/common" - "github.com/nunnatsa/piHatDraw/notifier" + "encoding/json" + "fmt" + "image" + "image/color" + "image/png" + "log" + "net/http" + "strconv" + + "github.com/gorilla/websocket" + + "github.com/nunnatsa/piHatDraw/common" + "github.com/nunnatsa/piHatDraw/notifier" ) var ( - upgrader = websocket.Upgrader{ - ReadBufferSize: 1500, - WriteBufferSize: 1500, - CheckOrigin: func(r *http.Request) bool { - return true - }, - } + upgrader = websocket.Upgrader{ + ReadBufferSize: 1500, + WriteBufferSize: 1500, + CheckOrigin: func(r *http.Request) bool { + return true + }, + } ) type ClientEvent any @@ -41,244 +41,244 @@ type ClientEventDownload chan [][]common.Color type ClientEventUndo bool type WebApplication struct { - mux *http.ServeMux - notifier *notifier.Notifier - clientEvents chan<- ClientEvent + mux *http.ServeMux + notifier *notifier.Notifier + clientEvents chan<- ClientEvent } func (ca WebApplication) GetMux() *http.ServeMux { - return ca.mux + return ca.mux } func NewWebApplication(mailbox *notifier.Notifier, ch chan<- ClientEvent) *WebApplication { - mux := http.NewServeMux() - ca := &WebApplication{mux: mux, notifier: mailbox, clientEvents: ch} - - mux.Handle("/", ui) - mux.Handle("/api/canvas/register", GetOnlyRequest(ca.register)) - mux.Handle("/api/canvas/color", PostOnlyRequest(ca.setColor)) - mux.Handle("/api/canvas/tool", PostOnlyRequest(ca.setTool)) - mux.Handle("/api/canvas/reset", PostOnlyRequest(ca.reset)) - mux.Handle("/api/canvas/download", GetOnlyRequest(ca.downloadImage)) - mux.Handle("/api/canvas/undo", PostOnlyRequest(ca.undo)) - - return ca + mux := http.NewServeMux() + ca := &WebApplication{mux: mux, notifier: mailbox, clientEvents: ch} + + mux.Handle("/", ui) + mux.Handle("/api/canvas/register", GetOnlyRequest(ca.register)) + mux.Handle("/api/canvas/color", PostOnlyRequest(ca.setColor)) + mux.Handle("/api/canvas/tool", PostOnlyRequest(ca.setTool)) + mux.Handle("/api/canvas/reset", PostOnlyRequest(ca.reset)) + mux.Handle("/api/canvas/download", GetOnlyRequest(ca.downloadImage)) + mux.Handle("/api/canvas/undo", PostOnlyRequest(ca.undo)) + + return ca } func (ca WebApplication) register(w http.ResponseWriter, r *http.Request) { - conn, err := upgrader.Upgrade(w, r, nil) // error ignored for sake of simplicity - if err != nil { - log.Println("Error:", err) - w.WriteHeader(500) - _, _ = w.Write([]byte(err.Error())) + conn, err := upgrader.Upgrade(w, r, nil) // error ignored for sake of simplicity + if err != nil { + log.Println("Error:", err) + w.WriteHeader(500) + _, _ = w.Write([]byte(err.Error())) - return - } + return + } - defer conn.Close() + defer conn.Close() - subscription := make(chan []byte, 1) + subscription := make(chan []byte, 1) - id := ca.notifier.Subscribe(subscription) - defer ca.notifier.Unsubscribe(id) - ca.clientEvents <- ClientEventRegistered(id) + id := ca.notifier.Subscribe(subscription) + defer ca.notifier.Unsubscribe(id) + ca.clientEvents <- ClientEventRegistered(id) - for js := range subscription { - log.Printf("got event; updating client %d\n", id) - if err := conn.WriteMessage(websocket.TextMessage, js); err != nil { - log.Printf("failed to send message to the client %d: %v\n", id, err) - return - } - } + for js := range subscription { + log.Printf("got event; updating client %d\n", id) + if err := conn.WriteMessage(websocket.TextMessage, js); err != nil { + log.Printf("failed to send message to the client %d: %v\n", id, err) + return + } + } - log.Printf("Connection %d is closed\n", id) + log.Printf("Connection %d is closed\n", id) } type setColorRq struct { - Color common.Color `json:"color"` + Color common.Color `json:"color"` } func (ca WebApplication) setColor(w http.ResponseWriter, r *http.Request) { - enc := json.NewDecoder(r.Body) - msg := &setColorRq{} - err := enc.Decode(msg) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - fmt.Fprint(w, `{"error": "can't parse json'"}`) - return - } - - log.Printf("Got set color request. Color = #%06x", msg.Color) - - clientEvent := ClientEventSetColor(msg.Color) - ca.clientEvents <- clientEvent + enc := json.NewDecoder(r.Body) + msg := &setColorRq{} + err := enc.Decode(msg) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprint(w, `{"error": "can't parse json'"}`) + return + } + + log.Printf("Got set color request. Color = #%06x", msg.Color) + + clientEvent := ClientEventSetColor(msg.Color) + ca.clientEvents <- clientEvent } type setToolRq struct { - ToolName string `json:"toolName"` + ToolName string `json:"toolName"` } func (ca WebApplication) setTool(w http.ResponseWriter, r *http.Request) { - enc := json.NewDecoder(r.Body) - msg := &setToolRq{} - err := enc.Decode(msg) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - fmt.Fprint(w, `{"error": "can't parse json'"}`) - return - } - - log.Printf("Got set tool request. tool name = %v", msg.ToolName) - - clientEvent := ClientEventSetTool(msg.ToolName) - ca.clientEvents <- clientEvent + enc := json.NewDecoder(r.Body) + msg := &setToolRq{} + err := enc.Decode(msg) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprint(w, `{"error": "can't parse json'"}`) + return + } + + log.Printf("Got set tool request. tool name = %v", msg.ToolName) + + clientEvent := ClientEventSetTool(msg.ToolName) + ca.clientEvents <- clientEvent } type resetRq struct { - Reset bool `json:"reset"` + Reset bool `json:"reset"` } func (ca WebApplication) reset(w http.ResponseWriter, r *http.Request) { - enc := json.NewDecoder(r.Body) - msg := &resetRq{} - err := enc.Decode(msg) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - fmt.Fprint(w, `{"error": "can't parse json'"}`) - return - } - - if msg.Reset { - log.Printf("Got reset request") - } - - clientEvent := ClientEventReset(true) - ca.clientEvents <- clientEvent + enc := json.NewDecoder(r.Body) + msg := &resetRq{} + err := enc.Decode(msg) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprint(w, `{"error": "can't parse json'"}`) + return + } + + if msg.Reset { + log.Printf("Got reset request") + } + + clientEvent := ClientEventReset(true) + ca.clientEvents <- clientEvent } func (ca WebApplication) downloadImage(w http.ResponseWriter, r *http.Request) { - err := r.ParseForm() - if err != nil { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusBadRequest) - fmt.Fprintln(w, `{"error": "wrong request"}`) - return - } - - pixelSizeStr := r.Form.Get("pixelSize") - pixelSize, err := strconv.Atoi(pixelSizeStr) - - if err != nil || pixelSize < 1 || pixelSize > 20 { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusBadRequest) - fmt.Fprintln(w, `{"error": "wrong pixel size"}`) - return - } - - fileName := r.Form.Get("fileName") - if fileName == "" { - fileName = "untitled.png" - } - - canvasChannel := make(chan [][]common.Color, 1) - defer close(canvasChannel) - ca.clientEvents <- ClientEventDownload(canvasChannel) - imageData := <-canvasChannel - - imageCanvas, err := getImageCanvas(imageData, pixelSize) - if err != nil { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, `{"error": "%v"}`, err) - return - } - - w.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, fileName)) - w.Header().Set("Content-Type", "image/png") - - log.Printf("downloading a file %s; pixel size = %d\n", fileName, pixelSize) - _ = png.Encode(w, imageCanvas) + err := r.ParseForm() + if err != nil { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintln(w, `{"error": "wrong request"}`) + return + } + + pixelSizeStr := r.Form.Get("pixelSize") + pixelSize, err := strconv.Atoi(pixelSizeStr) + + if err != nil || pixelSize < 1 || pixelSize > 20 { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintln(w, `{"error": "wrong pixel size"}`) + return + } + + fileName := r.Form.Get("fileName") + if fileName == "" { + fileName = "untitled.png" + } + + canvasChannel := make(chan [][]common.Color, 1) + defer close(canvasChannel) + ca.clientEvents <- ClientEventDownload(canvasChannel) + imageData := <-canvasChannel + + imageCanvas, err := getImageCanvas(imageData, pixelSize) + if err != nil { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusInternalServerError) + fmt.Fprintf(w, `{"error": "%v"}`, err) + return + } + + w.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, fileName)) + w.Header().Set("Content-Type", "image/png") + + log.Printf("downloading a file %s; pixel size = %d\n", fileName, pixelSize) + _ = png.Encode(w, imageCanvas) } type undoRq struct { - Undo bool `json:"undo"` + Undo bool `json:"undo"` } func (ca WebApplication) undo(w http.ResponseWriter, r *http.Request) { - enc := json.NewDecoder(r.Body) - msg := &undoRq{} - err := enc.Decode(msg) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - fmt.Fprint(w, `{"error": "can't undo'"}`) - return - } - - if msg.Undo { - log.Printf("Got undo request") - } - - clientEvent := ClientEventUndo(true) - ca.clientEvents <- clientEvent + enc := json.NewDecoder(r.Body) + msg := &undoRq{} + err := enc.Decode(msg) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + fmt.Fprint(w, `{"error": "can't undo'"}`) + return + } + + if msg.Undo { + log.Printf("Got undo request") + } + + clientEvent := ClientEventUndo(true) + ca.clientEvents <- clientEvent } func getImageCanvas(imageData [][]common.Color, pixelSize int) (*image.RGBA, error) { - height := len(imageData) * pixelSize - if height == 0 { - return nil, fmt.Errorf("can't get the data") - } - - width := len(imageData[0]) * pixelSize - if width == 0 { - return nil, fmt.Errorf("can't get the data") - } - - img := image.NewRGBA(image.Rect(0, 0, width, height)) - for y, line := range imageData { - for x, pixel := range line { - setPixel(img, x, y, toColor(pixel), pixelSize) - } - } - - return img, nil + height := len(imageData) * pixelSize + if height == 0 { + return nil, fmt.Errorf("can't get the data") + } + + width := len(imageData[0]) * pixelSize + if width == 0 { + return nil, fmt.Errorf("can't get the data") + } + + img := image.NewRGBA(image.Rect(0, 0, width, height)) + for y, line := range imageData { + for x, pixel := range line { + setPixel(img, x, y, toColor(pixel), pixelSize) + } + } + + return img, nil } func setPixel(img *image.RGBA, x int, y int, pixel color.Color, pixelSize int) { - x = x * pixelSize - y = y * pixelSize - for x1 := x; x1 < x+pixelSize; x1++ { - for y1 := y; y1 < y+pixelSize; y1++ { - img.Set(x1, y1, pixel) - } - } + x = x * pixelSize + y = y * pixelSize + for x1 := x; x1 < x+pixelSize; x1++ { + for y1 := y; y1 < y+pixelSize; y1++ { + img.Set(x1, y1, pixel) + } + } } func toColor(pixel common.Color) color.Color { - r := uint8((pixel >> 16) & 0xFF) - g := uint8((pixel >> 8) & 0xFF) - b := uint8(pixel & 0xFF) + r := uint8((pixel >> 16) & 0xFF) + g := uint8((pixel >> 8) & 0xFF) + b := uint8(pixel & 0xFF) - return color.RGBA{A: 0xFF, R: r, G: g, B: b} + return color.RGBA{A: 0xFF, R: r, G: g, B: b} } func GetOnlyRequest(next http.HandlerFunc) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodGet { - w.WriteHeader(http.StatusMethodNotAllowed) - return - } - - next(w, r) - }) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + next(w, r) + }) } func PostOnlyRequest(next http.HandlerFunc) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - w.WriteHeader(http.StatusMethodNotAllowed) - return - } - - next(w, r) - }) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + next(w, r) + }) } diff --git a/webapp/websocket_test.go b/webapp/websocket_test.go index 50c5150..f862c11 100644 --- a/webapp/websocket_test.go +++ b/webapp/websocket_test.go @@ -1,360 +1,359 @@ package webapp import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - neturl "net/url" - "strings" - "testing" - "time" - - "github.com/gorilla/websocket" - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" - - "github.com/nunnatsa/piHatDraw/common" - "github.com/nunnatsa/piHatDraw/notifier" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + neturl "net/url" + "strings" + "testing" + "time" + + "github.com/gorilla/websocket" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/nunnatsa/piHatDraw/common" + "github.com/nunnatsa/piHatDraw/notifier" ) func TestWebApp(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "webapp Suite") + RegisterFailHandler(Fail) + RunSpecs(t, "webapp Suite") } var _ = Describe("Test the web application", func() { - Context("test register request", func() { - - var ( - n *notifier.Notifier - server *httptest.Server - wa *WebApplication - ce chan ClientEvent - ) - - BeforeEach(func() { - n = notifier.NewNotifier() - ce = make(chan ClientEvent) - wa = NewWebApplication(n, ce) - server = httptest.NewServer(wa.GetMux()) - }) - - AfterEach(func() { - n.Close() - server.Close() - close(ce) - }) - - It("test register", func() { - url := "ws" + strings.TrimPrefix(server.URL, "http") + "/api/canvas/register" - - numClients := ClientEventRegistered(10) - sockets := make([]*websocket.Conn, 0, numClients) - message := "another message" - defer func() { - for _, ws := range sockets { - ws.Close() - } - }() - - for i := ClientEventRegistered(1); i <= numClients; i++ { - ws, _, err := websocket.DefaultDialer.Dial(url, nil) - Expect(err).ToNot(HaveOccurred()) - - sockets = append(sockets, ws) - - clientEvent := <-ce - subscriberID, ok := clientEvent.(ClientEventRegistered) - Expect(ok).To(BeTrue()) - Expect(subscriberID).Should(BeEquivalentTo(i)) - - n.NotifyOne(uint64(subscriberID), []byte(message)) - - _, p, err := ws.ReadMessage() - Expect(err).ToNot(HaveOccurred()) - Expect(string(p)).Should(Equal(message)) - } - - message = "hello there" - n.NotifyAll([]byte(message)) - - for _, ws := range sockets { - _, p, err := ws.ReadMessage() - Expect(err).ToNot(HaveOccurred()) - Expect(string(p)).Should(Equal(message)) - } - }) - - It("should reject if the method is wrong", func() { - url := server.URL + "/api/canvas/register" - - res, err := server.Client().Post(url, "text/plain", strings.NewReader("test request body")) - Expect(err).ShouldNot(HaveOccurred()) - Expect(res.StatusCode).Should(Equal(http.StatusMethodNotAllowed)) - }) - }) - - Context("test POST requests", func() { - var ( - n *notifier.Notifier - ce chan ClientEvent - wa *WebApplication - server *httptest.Server - ) - - BeforeEach(func() { - n = notifier.NewNotifier() - ce = make(chan ClientEvent, 1) - wa = NewWebApplication(n, ce) - server = httptest.NewServer(wa.GetMux()) - }) - - AfterEach(func() { - n.Close() - close(ce) - server.Close() - }) - - DescribeTable("send valid POST request", func(url, reqBody string, expected any) { - url = server.URL + url - - res, err := server.Client().Post(url, "application/json", strings.NewReader(reqBody)) - Expect(err).ToNot(HaveOccurred()) - Expect(res.StatusCode).Should(Equal(http.StatusOK)) - - Eventually(func() bool { - clientEvent := <-ce - Expect(clientEvent).Should(BeEquivalentTo(expected)) - return true - }, time.Millisecond*10, time.Millisecond).Should(BeTrue()) - }, - Entry("test set color request", "/api/canvas/color", `{"color": "#123456"}`, 0x123456), - Entry("test set tool request", "/api/canvas/tool", `{"toolName": "pen"}`, "pen"), - Entry("test reset request", "/api/canvas/reset", `{"reset": true}`, true), - Entry("test undo request", "/api/canvas/undo", `{"undo": true}`, true), - ) - - DescribeTable("should reject if not a POST request", func(url string) { - url = server.URL + url - - res, err := server.Client().Get(url) - Expect(err).ToNot(HaveOccurred()) - Expect(res.StatusCode).Should(Equal(http.StatusMethodNotAllowed)) - }, - Entry("wrong method in set color request", "/api/canvas/color"), - Entry("wrong method in set tool request", "/api/canvas/tool"), - Entry("wrong method in reset request", "/api/canvas/reset"), - Entry("wrong method in undo request", "/api/canvas/undo"), - ) - - DescribeTable("should reject if not the body is in wrong json format", func(url string) { - url = server.URL + url - - res, err := server.Client().Post(url, "application/json", strings.NewReader(`bad json`)) - Expect(err).ToNot(HaveOccurred()) - Expect(res.StatusCode).Should(Equal(http.StatusBadRequest)) - - }, - Entry("wrong json in set color request", "/api/canvas/color"), - Entry("wrong json in set tool request", "/api/canvas/tool"), - Entry("wrong json in reset request", "/api/canvas/reset"), - Entry("wrong json in undo request", "/api/canvas/undo"), - ) - }) - - Context("test download request", func() { - var ( - n *notifier.Notifier - ce chan ClientEvent - wa *WebApplication - server *httptest.Server - ) - - BeforeEach(func() { - n = notifier.NewNotifier() - ce = make(chan ClientEvent, 1) - wa = NewWebApplication(n, ce) - server = httptest.NewServer(wa.GetMux()) - }) - - AfterEach(func() { - n.Close() - close(ce) - server.Close() - }) - - DescribeTable("should download an image file", func(fileName string) { - done := make(chan bool) - - go sendImageData(ce, done) - - url := server.URL + "/api/canvas/download?pixelSize=3" - if len(fileName) > 0 { - url = fmt.Sprintf("%s&fileName=%s.png", url, fileName) - } else { - fileName = "untitled" - } - - res, err := server.Client().Get(url) - Expect(err).ToNot(HaveOccurred()) - Expect(res.StatusCode).Should(Equal(http.StatusOK)) - - Eventually(func() bool { return <-done }, time.Millisecond*10, time.Millisecond).Should(BeTrue()) - - Eventually(func() bool { - defer res.Body.Close() - - body, err := ioutil.ReadAll(res.Body) - Expect(err).ToNot(HaveOccurred()) - Expect(body).ToNot(BeEmpty()) - fileType := string(body[:4]) - Expect(fileType).Should(Equal("\x89PNG")) - - Expect(res.Header.Get("Content-Type")).Should(Equal("image/png")) - expected := fmt.Sprintf(`attachment; filename="%s.png"`, fileName) - Expect(res.Header.Get("Content-Disposition")).Should(Equal(expected)) - - return true - }).Should(BeTrue()) - }, - Entry("with file name", "test"), - Entry("without file name", ""), - ) - - DescribeTable("should return error if the data is empty", func(data [][]common.Color) { - done := make(chan bool) - - go func() { - defer close(done) - clientEvent := <-ce - - cb, ok := clientEvent.(ClientEventDownload) - if !ok { - done <- false - return - } - Expect(ok).Should(BeTrue()) - - cb <- data - done <- true - }() - - url := server.URL + "/api/canvas/download?pixelSize=3" - res, err := server.Client().Get(url) - - Eventually(func() bool { return <-done }, time.Millisecond*10, time.Millisecond).Should(BeTrue()) - - Expect(err).ToNot(HaveOccurred()) - Expect(res.StatusCode).Should(Equal(http.StatusInternalServerError)) - - Eventually(func() bool { - defer res.Body.Close() - - Expect(res.Header.Get("Content-Type")).Should(Equal("application/json")) - - dec := json.NewDecoder(res.Body) - errMsg := &errorResponse{} - err = dec.Decode(errMsg) - Expect(err).ToNot(HaveOccurred()) - Expect(errMsg.Error).Should(Equal("can't get the data")) - - return true - }).Should(BeTrue()) - }, - Entry("no data received", nil), - Entry("empty data received", [][]common.Color{}), - Entry("empty lines received", [][]common.Color{{}, {}, {}, {}}), - ) - - It("should return error if there is no pixelSize query parameter", func() { - url := server.URL + "/api/canvas/download" - res, err := server.Client().Get(url) - - Consistently(ce).ShouldNot(Receive()) - - Eventually(func() bool { - defer res.Body.Close() - Expect(err).ToNot(HaveOccurred()) - Expect(res.StatusCode).Should(Equal(http.StatusBadRequest)) - - Expect(res.Header.Get("Content-Type")).Should(Equal("application/json")) - - dec := json.NewDecoder(res.Body) - errMsg := &errorResponse{} - err = dec.Decode(errMsg) - Expect(err).ToNot(HaveOccurred()) - Expect(errMsg.Error).Should(Equal("wrong pixel size")) - - return true - }).Should(BeTrue()) - }) - - It("should return error if there is the pixelSize is too big", func() { - url := server.URL + "/api/canvas/download?pixelSize=50" - res, err := server.Client().Get(url) - - Consistently(ce).ShouldNot(Receive()) - - Eventually(func() bool { - defer res.Body.Close() - Expect(err).ToNot(HaveOccurred()) - Expect(res.StatusCode).Should(Equal(http.StatusBadRequest)) - - Expect(res.Header.Get("Content-Type")).Should(Equal("application/json")) - - dec := json.NewDecoder(res.Body) - errMsg := &errorResponse{} - err = dec.Decode(errMsg) - Expect(err).ToNot(HaveOccurred()) - Expect(errMsg.Error).Should(Equal("wrong pixel size")) - - return true - }).Should(BeTrue()) - }) - - It("should return error if the method is wrong", func() { - url := server.URL + "/api/canvas/download" - form := neturl.Values{ - "pixelSize": []string{"3"}, - "fileName": []string{"test.png"}, - } - res, err := server.Client().PostForm(url, form) - Expect(err).ToNot(HaveOccurred()) - Expect(res.StatusCode).Should(Equal(http.StatusMethodNotAllowed)) - - Consistently(ce).ShouldNot(Receive()) - }) - }) + Context("test register request", func() { + + var ( + n *notifier.Notifier + server *httptest.Server + wa *WebApplication + ce chan ClientEvent + ) + + BeforeEach(func() { + n = notifier.NewNotifier() + ce = make(chan ClientEvent) + wa = NewWebApplication(n, ce) + server = httptest.NewServer(wa.GetMux()) + }) + + AfterEach(func() { + n.Close() + server.Close() + close(ce) + }) + + It("test register", func() { + url := "ws" + strings.TrimPrefix(server.URL, "http") + "/api/canvas/register" + + numClients := ClientEventRegistered(10) + sockets := make([]*websocket.Conn, 0, numClients) + message := "another message" + defer func() { + for _, ws := range sockets { + ws.Close() + } + }() + + for i := ClientEventRegistered(1); i <= numClients; i++ { + ws, _, err := websocket.DefaultDialer.Dial(url, nil) + Expect(err).ToNot(HaveOccurred()) + + sockets = append(sockets, ws) + + clientEvent := <-ce + subscriberID, ok := clientEvent.(ClientEventRegistered) + Expect(ok).To(BeTrue()) + Expect(subscriberID).Should(BeEquivalentTo(i)) + + n.NotifyOne(uint64(subscriberID), []byte(message)) + + _, p, err := ws.ReadMessage() + Expect(err).ToNot(HaveOccurred()) + Expect(string(p)).Should(Equal(message)) + } + + message = "hello there" + n.NotifyAll([]byte(message)) + + for _, ws := range sockets { + _, p, err := ws.ReadMessage() + Expect(err).ToNot(HaveOccurred()) + Expect(string(p)).Should(Equal(message)) + } + }) + + It("should reject if the method is wrong", func() { + url := server.URL + "/api/canvas/register" + + res, err := server.Client().Post(url, "text/plain", strings.NewReader("test request body")) + Expect(err).ShouldNot(HaveOccurred()) + Expect(res.StatusCode).Should(Equal(http.StatusMethodNotAllowed)) + }) + }) + + Context("test POST requests", func() { + var ( + n *notifier.Notifier + ce chan ClientEvent + wa *WebApplication + server *httptest.Server + ) + + BeforeEach(func() { + n = notifier.NewNotifier() + ce = make(chan ClientEvent, 1) + wa = NewWebApplication(n, ce) + server = httptest.NewServer(wa.GetMux()) + }) + + AfterEach(func() { + n.Close() + close(ce) + server.Close() + }) + + DescribeTable("send valid POST request", func(url, reqBody string, expected any) { + url = server.URL + url + + res, err := server.Client().Post(url, "application/json", strings.NewReader(reqBody)) + Expect(err).ToNot(HaveOccurred()) + Expect(res.StatusCode).Should(Equal(http.StatusOK)) + + Eventually(func() bool { + clientEvent := <-ce + Expect(clientEvent).Should(BeEquivalentTo(expected)) + return true + }, time.Millisecond*10, time.Millisecond).Should(BeTrue()) + }, + Entry("test set color request", "/api/canvas/color", `{"color": "#123456"}`, 0x123456), + Entry("test set tool request", "/api/canvas/tool", `{"toolName": "pen"}`, "pen"), + Entry("test reset request", "/api/canvas/reset", `{"reset": true}`, true), + Entry("test undo request", "/api/canvas/undo", `{"undo": true}`, true), + ) + + DescribeTable("should reject if not a POST request", func(url string) { + url = server.URL + url + + res, err := server.Client().Get(url) + Expect(err).ToNot(HaveOccurred()) + Expect(res.StatusCode).Should(Equal(http.StatusMethodNotAllowed)) + }, + Entry("wrong method in set color request", "/api/canvas/color"), + Entry("wrong method in set tool request", "/api/canvas/tool"), + Entry("wrong method in reset request", "/api/canvas/reset"), + Entry("wrong method in undo request", "/api/canvas/undo"), + ) + + DescribeTable("should reject if not the body is in wrong json format", func(url string) { + url = server.URL + url + + res, err := server.Client().Post(url, "application/json", strings.NewReader(`bad json`)) + Expect(err).ToNot(HaveOccurred()) + Expect(res.StatusCode).Should(Equal(http.StatusBadRequest)) + + }, + Entry("wrong json in set color request", "/api/canvas/color"), + Entry("wrong json in set tool request", "/api/canvas/tool"), + Entry("wrong json in reset request", "/api/canvas/reset"), + Entry("wrong json in undo request", "/api/canvas/undo"), + ) + }) + + Context("test download request", func() { + var ( + n *notifier.Notifier + ce chan ClientEvent + wa *WebApplication + server *httptest.Server + ) + + BeforeEach(func() { + n = notifier.NewNotifier() + ce = make(chan ClientEvent, 1) + wa = NewWebApplication(n, ce) + server = httptest.NewServer(wa.GetMux()) + }) + + AfterEach(func() { + n.Close() + close(ce) + server.Close() + }) + + DescribeTable("should download an image file", func(fileName string) { + done := make(chan bool) + + go sendImageData(ce, done) + + url := server.URL + "/api/canvas/download?pixelSize=3" + if len(fileName) > 0 { + url = fmt.Sprintf("%s&fileName=%s.png", url, fileName) + } else { + fileName = "untitled" + } + + res, err := server.Client().Get(url) + Expect(err).ToNot(HaveOccurred()) + Expect(res.StatusCode).Should(Equal(http.StatusOK)) + + Eventually(func() bool { return <-done }, time.Millisecond*10, time.Millisecond).Should(BeTrue()) + + Eventually(func() bool { + defer res.Body.Close() + + body, err := ioutil.ReadAll(res.Body) + Expect(err).ToNot(HaveOccurred()) + Expect(body).ToNot(BeEmpty()) + fileType := string(body[:4]) + Expect(fileType).Should(Equal("\x89PNG")) + + Expect(res.Header.Get("Content-Type")).Should(Equal("image/png")) + expected := fmt.Sprintf(`attachment; filename="%s.png"`, fileName) + Expect(res.Header.Get("Content-Disposition")).Should(Equal(expected)) + + return true + }).Should(BeTrue()) + }, + Entry("with file name", "test"), + Entry("without file name", ""), + ) + + DescribeTable("should return error if the data is empty", func(data [][]common.Color) { + done := make(chan bool) + + go func() { + defer close(done) + clientEvent := <-ce + + cb, ok := clientEvent.(ClientEventDownload) + if !ok { + done <- false + return + } + Expect(ok).Should(BeTrue()) + + cb <- data + done <- true + }() + + url := server.URL + "/api/canvas/download?pixelSize=3" + res, err := server.Client().Get(url) + + Eventually(func() bool { return <-done }, time.Millisecond*10, time.Millisecond).Should(BeTrue()) + + Expect(err).ToNot(HaveOccurred()) + Expect(res.StatusCode).Should(Equal(http.StatusInternalServerError)) + + Eventually(func() bool { + defer res.Body.Close() + + Expect(res.Header.Get("Content-Type")).Should(Equal("application/json")) + + dec := json.NewDecoder(res.Body) + errMsg := &errorResponse{} + err = dec.Decode(errMsg) + Expect(err).ToNot(HaveOccurred()) + Expect(errMsg.Error).Should(Equal("can't get the data")) + + return true + }).Should(BeTrue()) + }, + Entry("no data received", nil), + Entry("empty data received", [][]common.Color{}), + Entry("empty lines received", [][]common.Color{{}, {}, {}, {}}), + ) + + It("should return error if there is no pixelSize query parameter", func() { + url := server.URL + "/api/canvas/download" + res, err := server.Client().Get(url) + + Consistently(ce).ShouldNot(Receive()) + + Eventually(func() bool { + defer res.Body.Close() + Expect(err).ToNot(HaveOccurred()) + Expect(res.StatusCode).Should(Equal(http.StatusBadRequest)) + + Expect(res.Header.Get("Content-Type")).Should(Equal("application/json")) + + dec := json.NewDecoder(res.Body) + errMsg := &errorResponse{} + err = dec.Decode(errMsg) + Expect(err).ToNot(HaveOccurred()) + Expect(errMsg.Error).Should(Equal("wrong pixel size")) + + return true + }).Should(BeTrue()) + }) + + It("should return error if there is the pixelSize is too big", func() { + url := server.URL + "/api/canvas/download?pixelSize=50" + res, err := server.Client().Get(url) + + Consistently(ce).ShouldNot(Receive()) + + Eventually(func() bool { + defer res.Body.Close() + Expect(err).ToNot(HaveOccurred()) + Expect(res.StatusCode).Should(Equal(http.StatusBadRequest)) + + Expect(res.Header.Get("Content-Type")).Should(Equal("application/json")) + + dec := json.NewDecoder(res.Body) + errMsg := &errorResponse{} + err = dec.Decode(errMsg) + Expect(err).ToNot(HaveOccurred()) + Expect(errMsg.Error).Should(Equal("wrong pixel size")) + + return true + }).Should(BeTrue()) + }) + + It("should return error if the method is wrong", func() { + url := server.URL + "/api/canvas/download" + form := neturl.Values{ + "pixelSize": []string{"3"}, + "fileName": []string{"test.png"}, + } + res, err := server.Client().PostForm(url, form) + Expect(err).ToNot(HaveOccurred()) + Expect(res.StatusCode).Should(Equal(http.StatusMethodNotAllowed)) + + Consistently(ce).ShouldNot(Receive()) + }) + }) }) type errorResponse struct { - Error string `json:"error,omitempty"` + Error string `json:"error,omitempty"` } func sendImageData(ce <-chan ClientEvent, done chan<- bool) { - defer close(done) - clientEvent := <-ce - - cb, ok := clientEvent.(ClientEventDownload) - if !ok { - done <- false - return - } - Expect(ok).Should(BeTrue()) - - cb <- [][]common.Color{ - {0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0}, - {0, 0xFFFFFF, 0, 0, 0, 0, 0xFFFFFF, 0}, - {0, 0xFFFFFF, 0, 0, 0, 0, 0xFFFFFF, 0}, - {0, 0xFFFFFF, 0, 0, 0, 0, 0xFFFFFF, 0}, - {0, 0xFFFFFF, 0, 0, 0, 0, 0xFFFFFF, 0}, - {0, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, - } - done <- true + defer close(done) + clientEvent := <-ce + + cb, ok := clientEvent.(ClientEventDownload) + if !ok { + done <- false + return + } + Expect(ok).Should(BeTrue()) + + cb <- [][]common.Color{ + {0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0}, + {0, 0xFFFFFF, 0, 0, 0, 0, 0xFFFFFF, 0}, + {0, 0xFFFFFF, 0, 0, 0, 0, 0xFFFFFF, 0}, + {0, 0xFFFFFF, 0, 0, 0, 0, 0xFFFFFF, 0}, + {0, 0xFFFFFF, 0, 0, 0, 0, 0xFFFFFF, 0}, + {0, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0}, + {0, 0, 0, 0, 0, 0, 0, 0}, + } + done <- true }