Compare commits
77 Commits
0c6f686ce5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
c3be0b8e54
|
|||
|
8ca7858069
|
|||
|
5e030a5bc3
|
|||
|
b47abbfc0b
|
|||
|
4905d18222
|
|||
|
eb8838ab75
|
|||
|
6ad0112683
|
|||
|
b9cea2347c
|
|||
|
e420bf303a
|
|||
|
92ddd9e0fe
|
|||
|
8c17aa9d6d
|
|||
|
d37c53c60b
|
|||
|
2465b0a73a
|
|||
| 12515c2a1e | |||
|
6dd91f5a1b
|
|||
|
249fabbd7a
|
|||
|
c9675b1573
|
|||
|
63a9e2ff58
|
|||
|
28325c8d7b
|
|||
|
93a5e9c1ba
|
|||
|
4d093ed99a
|
|||
|
77e6e6bc04
|
|||
|
c28939d2b8
|
|||
|
cfe5d89b22
|
|||
|
f84c9b6ea2
|
|||
|
42d808165b
|
|||
|
96b22a2254
|
|||
|
c1ae0706f9
|
|||
|
8eff1115c5
|
|||
|
4708a2b8ff
|
|||
|
4989848335
|
|||
|
d02d48e7c8
|
|||
|
2a3d789292
|
|||
|
169afed5c9
|
|||
|
3648d5a1cc
|
|||
|
1c3555d8b7
|
|||
|
d8604dc3cc
|
|||
|
417a7cf982
|
|||
|
ab1dab6619
|
|||
|
262321a1e2
|
|||
|
c8b8f87f6c
|
|||
|
f59593e9e8
|
|||
|
c8e33884c0
|
|||
|
55c8c739b0
|
|||
|
1d45635530
|
|||
|
3ea8603368
|
|||
|
c0175fc9bc
|
|||
| 7e7558dd5e | |||
|
86a4a28aee
|
|||
|
a5528be456
|
|||
| 0faef20441 | |||
| 395231e1bf | |||
| bc68115ce1 | |||
| dfb5ead740 | |||
| 2c630aff95 | |||
| dc18440821 | |||
| 4b4070df98 | |||
| 325aa46cf2 | |||
|
ebee8a304b
|
|||
|
3f13118312
|
|||
|
27cdde132d
|
|||
|
f8b1949c28
|
|||
|
9b276bd644
|
|||
|
70ddd91d6b
|
|||
|
95d945dda7
|
|||
|
bbb0cf3f42
|
|||
|
cd7f919eb2
|
|||
|
e77d3c4d5e
|
|||
|
0e16bb5361
|
|||
|
2bda056ca7
|
|||
|
6d104dc72a
|
|||
|
72e3fbe05b
|
|||
|
acf793ae41
|
|||
|
e6158e680f
|
|||
|
9f5f5413d1
|
|||
|
9c56ea56e2
|
|||
|
b6a6c9375f
|
@@ -1,7 +1,7 @@
|
|||||||
app:
|
app:
|
||||||
environment: "devel"
|
environment: "devel"
|
||||||
port: 5151
|
port: 5151
|
||||||
|
timezone: Africa/Abidjan
|
||||||
db:
|
db:
|
||||||
type: "aoeu"
|
type: "aoeu"
|
||||||
host: "aeihn"
|
host: "aeihn"
|
||||||
@@ -9,3 +9,4 @@ db:
|
|||||||
user: USER
|
user: USER
|
||||||
password: PASSWORD
|
password: PASSWORD
|
||||||
database: g2
|
database: g2
|
||||||
|
droponstart: true # don't use this in production!
|
||||||
|
|||||||
@@ -10,16 +10,18 @@ import (
|
|||||||
type AppConfig struct {
|
type AppConfig struct {
|
||||||
Environment string
|
Environment string
|
||||||
Port string
|
Port string
|
||||||
|
Timezone string
|
||||||
}
|
}
|
||||||
|
|
||||||
// DBConfig is the config for the DB connection.
|
// DBConfig is the config for the DB connection.
|
||||||
type DBConfig struct {
|
type DBConfig struct {
|
||||||
Type string
|
Type string
|
||||||
Host string
|
Host string
|
||||||
Port string
|
Port string
|
||||||
User string
|
User string
|
||||||
Password string
|
Password string
|
||||||
Database string
|
Database string
|
||||||
|
DropOnStart bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conf represents the overall configuration of the application.
|
// Conf represents the overall configuration of the application.
|
||||||
@@ -33,14 +35,16 @@ func createDefaultConf() *Conf {
|
|||||||
App: AppConfig{
|
App: AppConfig{
|
||||||
Environment: "local",
|
Environment: "local",
|
||||||
Port: "8080",
|
Port: "8080",
|
||||||
|
Timezone: "America/New_York",
|
||||||
},
|
},
|
||||||
Db: DBConfig{
|
Db: DBConfig{
|
||||||
Type: "postgres",
|
Type: "postgres",
|
||||||
Host: "localhost",
|
Host: "localhost",
|
||||||
Port: "5432",
|
Port: "5432",
|
||||||
User: "<user>",
|
User: "<user>",
|
||||||
Password: "<password>",
|
Password: "<password>",
|
||||||
Database: "gogmagog",
|
Database: "gogmagog",
|
||||||
|
DropOnStart: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ func TestSample(t *testing.T) {
|
|||||||
appConf := conf.App
|
appConf := conf.App
|
||||||
assert.Equal("devel", appConf.Environment)
|
assert.Equal("devel", appConf.Environment)
|
||||||
assert.Equal("5151", appConf.Port)
|
assert.Equal("5151", appConf.Port)
|
||||||
|
assert.Equal("Africa/Abidjan", appConf.Timezone)
|
||||||
|
|
||||||
dbConf := conf.Db
|
dbConf := conf.Db
|
||||||
assert.Equal("aoeu", dbConf.Type)
|
assert.Equal("aoeu", dbConf.Type)
|
||||||
@@ -24,6 +25,7 @@ func TestSample(t *testing.T) {
|
|||||||
assert.Equal("USER", dbConf.User)
|
assert.Equal("USER", dbConf.User)
|
||||||
assert.Equal("PASSWORD", dbConf.Password)
|
assert.Equal("PASSWORD", dbConf.Password)
|
||||||
assert.Equal("g2", dbConf.Database)
|
assert.Equal("g2", dbConf.Database)
|
||||||
|
assert.True(dbConf.DropOnStart)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefault(t *testing.T) {
|
func TestDefault(t *testing.T) {
|
||||||
@@ -35,6 +37,7 @@ func TestDefault(t *testing.T) {
|
|||||||
appConf := conf.App
|
appConf := conf.App
|
||||||
assert.Equal("missingfield", appConf.Environment)
|
assert.Equal("missingfield", appConf.Environment)
|
||||||
assert.Equal("8080", appConf.Port)
|
assert.Equal("8080", appConf.Port)
|
||||||
|
assert.Equal("America/New_York", appConf.Timezone)
|
||||||
|
|
||||||
dbConf := conf.Db
|
dbConf := conf.Db
|
||||||
assert.Equal("typical", dbConf.Type)
|
assert.Equal("typical", dbConf.Type)
|
||||||
@@ -43,6 +46,7 @@ func TestDefault(t *testing.T) {
|
|||||||
assert.Equal("<user>", dbConf.User)
|
assert.Equal("<user>", dbConf.User)
|
||||||
assert.Equal("<password>", dbConf.Password)
|
assert.Equal("<password>", dbConf.Password)
|
||||||
assert.Equal("gogmagog", dbConf.Database)
|
assert.Equal("gogmagog", dbConf.Database)
|
||||||
|
assert.False(dbConf.DropOnStart)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMissingFile(t *testing.T) {
|
func TestMissingFile(t *testing.T) {
|
||||||
|
|||||||
2
do.sh
2
do.sh
@@ -12,7 +12,7 @@ build() {
|
|||||||
|
|
||||||
test() {
|
test() {
|
||||||
echo "I am ${FUNCNAME[0]}ing"
|
echo "I am ${FUNCNAME[0]}ing"
|
||||||
_lint && _vet && _test
|
fmt && _lint && _vet && _test
|
||||||
}
|
}
|
||||||
|
|
||||||
run() {
|
run() {
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -4,9 +4,13 @@ go 1.15
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||||
|
github.com/go-chi/chi v4.1.2+incompatible
|
||||||
|
github.com/go-chi/jwtauth v1.1.1
|
||||||
github.com/golang-migrate/migrate/v4 v4.14.1
|
github.com/golang-migrate/migrate/v4 v4.14.1
|
||||||
github.com/jackc/pgx/v4 v4.10.1
|
github.com/jackc/pgx/v4 v4.10.1
|
||||||
github.com/jmoiron/sqlx v1.2.0
|
github.com/jmoiron/sqlx v1.2.0
|
||||||
|
github.com/lestrrat-go/jwx v1.0.6-0.20201127121120-26218808f029
|
||||||
github.com/spf13/viper v1.7.1
|
github.com/spf13/viper v1.7.1
|
||||||
github.com/stretchr/testify v1.5.1
|
github.com/stretchr/testify v1.5.1
|
||||||
|
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
|
||||||
)
|
)
|
||||||
|
|||||||
11
go.sum
11
go.sum
@@ -108,6 +108,11 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
|
|||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw=
|
github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
|
||||||
|
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
|
||||||
|
github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||||
|
github.com/go-chi/jwtauth v1.1.1 h1:CtUHwzvXUfZeZSbASLgzaTZQ8mL7p+vitX59NBTL1vY=
|
||||||
|
github.com/go-chi/jwtauth v1.1.1/go.mod h1:znOWz9e5/GfBOKiZlOUoEfjSjUF+cLZO3GcpkoGXvFI=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
@@ -305,6 +310,11 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
|||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
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/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4=
|
github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4=
|
||||||
|
github.com/lestrrat-go/iter v0.0.0-20200422075355-fc1769541911 h1:FvnrqecqX4zT0wOIbYK1gNgTm0677INEWiFY8UEYggY=
|
||||||
|
github.com/lestrrat-go/iter v0.0.0-20200422075355-fc1769541911/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
|
||||||
|
github.com/lestrrat-go/jwx v1.0.6-0.20201127121120-26218808f029 h1:+HTAqhgKkKqizghOYb4uEpZ7wK8tl3Y48ZbUTHF521c=
|
||||||
|
github.com/lestrrat-go/jwx v1.0.6-0.20201127121120-26218808f029/go.mod h1:TPF17WiSFegZo+c20fdpw49QD+/7n4/IsGvEmCSWwT0=
|
||||||
|
github.com/lestrrat-go/pdebug v0.0.0-20200204225717-4d6bd78da58d/go.mod h1:B06CSso/AWxiPejj+fheUINGeBKeeEZNt8w+EoU7+L8=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
@@ -649,6 +659,7 @@ golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapK
|
|||||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||||
|
golang.org/x/tools v0.0.0-20200417140056-c07e33ef3290/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
|||||||
30
main.go
30
main.go
@@ -3,8 +3,11 @@ package main
|
|||||||
import (
|
import (
|
||||||
"gitea.deepak.science/deepak/gogmagog/config"
|
"gitea.deepak.science/deepak/gogmagog/config"
|
||||||
"gitea.deepak.science/deepak/gogmagog/models"
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
"gitea.deepak.science/deepak/gogmagog/store"
|
"gitea.deepak.science/deepak/gogmagog/store"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,7 +15,7 @@ func main() {
|
|||||||
// Config
|
// Config
|
||||||
conf, err := config.GetConf("config")
|
conf, err := config.GetConf("config")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Could not get config", err)
|
log.Println("Could not get config", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,32 +23,27 @@ func main() {
|
|||||||
port := appConf.Port
|
port := appConf.Port
|
||||||
env := appConf.Environment
|
env := appConf.Environment
|
||||||
|
|
||||||
log.Print("Running server on " + port)
|
|
||||||
log.Print("App environment is " + env)
|
|
||||||
|
|
||||||
// DB
|
// DB
|
||||||
store, err := store.GetStore(&conf.Db)
|
store, err := store.GetStore(&conf.Db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Could not get store", err)
|
log.Println("Could not get store", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if store != nil {
|
if store != nil {
|
||||||
log.Print("Got DB connection")
|
log.Println("Got DB connection")
|
||||||
}
|
}
|
||||||
m := models.New(store)
|
m := models.New(store)
|
||||||
if m != nil {
|
if m != nil {
|
||||||
log.Print("created model")
|
log.Println("created model")
|
||||||
}
|
}
|
||||||
|
|
||||||
acts, acterr := m.Actions()
|
router := routes.NewRouter(m, tokens.New("my secret"))
|
||||||
if acterr != nil {
|
|
||||||
log.Fatal("whoopsies", acterr)
|
|
||||||
} else {
|
|
||||||
log.Printf("Got %d actions", len(acts))
|
|
||||||
for i, act := range acts {
|
|
||||||
log.Printf("%d: %v", i, act)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
log.Println("Running server on " + port)
|
||||||
|
|
||||||
|
http.ListenAndServe(":"+port, router)
|
||||||
|
|
||||||
|
log.Println("App environment is " + env)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,22 +6,34 @@ import (
|
|||||||
|
|
||||||
// Action represents a single action item.
|
// Action represents a single action item.
|
||||||
type Action struct {
|
type Action struct {
|
||||||
ActionID int64
|
ActionID int64 `json:"action_id"`
|
||||||
ActionDescription string
|
ActionDescription string `json:"action_description"`
|
||||||
EstimatedChunks int
|
UserID int64 `json:"user_id"`
|
||||||
CompletedChunks int
|
EstimatedChunks int `json:"estimated_chunks"`
|
||||||
CompletedOn time.Time
|
CompletedChunks int `json:"completed_chunks"`
|
||||||
PlanID int
|
CompletedOn *time.Time `json:"completed_on,omitempty"`
|
||||||
CreatedAt time.Time
|
PlanID int `json:"plan_id"`
|
||||||
UpdatedAt time.Time
|
CreatedAt *time.Time `json:"created_at,omitempty"`
|
||||||
|
UpdatedAt *time.Time `json:"updated_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions returns all actions from the model.
|
// Actions returns all actions from the model.
|
||||||
func (m *Model) Actions() ([]*Action, error) {
|
func (m *Model) Actions(userID int) ([]*Action, error) {
|
||||||
return m.SelectActions()
|
return m.SelectActions(userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action returns a single action from its ID
|
// Action returns a single action from its ID
|
||||||
func (m *Model) Action(id int) (*Action, error) {
|
func (m *Model) Action(id int, userID int) (*Action, error) {
|
||||||
return m.SelectActionByID(id)
|
act, err := m.SelectActionByID(id, userID)
|
||||||
|
return act, wrapNotFound(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddAction inserts a given action into the store, returning the generated ActionID. The provided ActionID is ignored.
|
||||||
|
func (m *Model) AddAction(action *Action, userID int) (int, error) {
|
||||||
|
return m.InsertAction(action, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveAction saves and updates an action.
|
||||||
|
func (m *Model) SaveAction(action *Action, userID int) error {
|
||||||
|
return m.UpdateAction(action, userID)
|
||||||
}
|
}
|
||||||
|
|||||||
23
models/current_plan.go
Normal file
23
models/current_plan.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
// CurrentPlan represents the primary plan ID for a particular user ID.
|
||||||
|
type CurrentPlan struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
PlanID int64 `json:"plan_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentPlan returns the primary plan for a provided user ID in the given model.
|
||||||
|
func (m *Model) CurrentPlan(userID int) (*CurrentPlan, error) {
|
||||||
|
pp, err := m.SelectCurrentPlan(userID)
|
||||||
|
return pp, wrapNotFound(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddCurrentPlan inserts a given primary plan into the store, returning nothing.
|
||||||
|
func (m *Model) AddCurrentPlan(pp *CurrentPlan, userID int) error {
|
||||||
|
return m.InsertCurrentPlan(pp, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveCurrentPlan saves and updates a primary plan.
|
||||||
|
func (m *Model) SaveCurrentPlan(pp *CurrentPlan, userID int) error {
|
||||||
|
return m.UpdateCurrentPlan(pp, userID)
|
||||||
|
}
|
||||||
45
models/current_plan_test.go
Normal file
45
models/current_plan_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package models_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/store"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestModelCurrentPlan(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
a1 := &models.Action{ActionID: 3}
|
||||||
|
userID := 3
|
||||||
|
p := &models.Plan{PlanID: 6}
|
||||||
|
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
str.InsertAction(a1, userID)
|
||||||
|
str.InsertPlan(p, userID)
|
||||||
|
str.InsertPlan(p, userID)
|
||||||
|
m := models.New(str)
|
||||||
|
|
||||||
|
_, err := m.CurrentPlan(userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
assert.True(models.IsNotFoundError(err))
|
||||||
|
|
||||||
|
err = m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
pp, err := m.CurrentPlan(userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(1, pp.PlanID)
|
||||||
|
assert.EqualValues(userID, pp.UserID)
|
||||||
|
|
||||||
|
err = m.AddCurrentPlan(&models.CurrentPlan{PlanID: 2}, userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
err = m.SaveCurrentPlan(&models.CurrentPlan{PlanID: 2}, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
pp, err = m.CurrentPlan(userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(2, pp.PlanID)
|
||||||
|
assert.EqualValues(userID, pp.UserID)
|
||||||
|
|
||||||
|
}
|
||||||
11
models/err_model_test.go
Normal file
11
models/err_model_test.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package models_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getErrorModel(err error) *models.Model {
|
||||||
|
str := store.GetErrorStoreForError(err, true)
|
||||||
|
return models.New(str)
|
||||||
|
}
|
||||||
54
models/errors.go
Normal file
54
models/errors.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type notFoundError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *notFoundError) NotFound() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotFoundError returns true if the model deems it a not found error.
|
||||||
|
func IsNotFoundError(err error) bool {
|
||||||
|
type notFound interface {
|
||||||
|
NotFound() bool
|
||||||
|
}
|
||||||
|
te, ok := err.(notFound)
|
||||||
|
return ok && te.NotFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapNotFound(err error) error {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return ¬FoundError{error: err}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type invalidLoginError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *invalidLoginError) InvalidLogin() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInvalidLoginError returns true if the model deems it an invalid login error.
|
||||||
|
func IsInvalidLoginError(err error) bool {
|
||||||
|
type invalidLogin interface {
|
||||||
|
InvalidLogin() bool
|
||||||
|
}
|
||||||
|
te, ok := err.(invalidLogin)
|
||||||
|
return ok && te.InvalidLogin()
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrapInvalidLogin(err error) error {
|
||||||
|
if err == sql.ErrNoRows || err == bcrypt.ErrMismatchedHashAndPassword {
|
||||||
|
return &invalidLoginError{error: err}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
49
models/errors_test.go
Normal file
49
models/errors_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package models_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRandomErrNotNotFound(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
err := fmt.Errorf("example")
|
||||||
|
|
||||||
|
assert.False(models.IsNotFoundError(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MyError) NotFound() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCustomInterface(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
err := &MyError{fmt.Errorf("example")}
|
||||||
|
|
||||||
|
assert.True(models.IsNotFoundError(err))
|
||||||
|
}
|
||||||
|
func TestErrorModelWrapping(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getErrorModel(sql.ErrNoRows)
|
||||||
|
|
||||||
|
_, err := m.Plan(0, 0)
|
||||||
|
assert.True(models.IsNotFoundError(err))
|
||||||
|
_, err = m.Action(0, 0)
|
||||||
|
assert.True(models.IsNotFoundError(err))
|
||||||
|
}
|
||||||
|
func TestErrorModelInvalidLogin(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getErrorModel(sql.ErrNoRows)
|
||||||
|
|
||||||
|
_, err := m.VerifyUserByUsernamePassword("duck", "duck")
|
||||||
|
assert.True(models.IsInvalidLoginError(err))
|
||||||
|
}
|
||||||
@@ -1,11 +1,26 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// Store represents the backing store.
|
// Store represents the backing store.
|
||||||
type Store interface {
|
type Store interface {
|
||||||
SelectActions() ([]*Action, error)
|
ConnectionLive() error
|
||||||
SelectActionByID(id int) (*Action, error)
|
SelectActions(userID int) ([]*Action, error)
|
||||||
SelectPlans() ([]*Plan, error)
|
SelectActionByID(id int, userID int) (*Action, error)
|
||||||
SelectPlanByID(id int) (*Plan, error)
|
InsertAction(action *Action, userID int) (int, error)
|
||||||
|
UpdateAction(action *Action, userID int) error
|
||||||
|
SelectPlans(userID int) ([]*Plan, error)
|
||||||
|
SelectPlanByID(id int, userID int) (*Plan, error)
|
||||||
|
InsertPlan(plan *Plan, userID int) (int, error)
|
||||||
|
UpdatePlan(plan *Plan, userID int) error
|
||||||
|
SelectActionsByPlanID(plan *Plan, userID int) ([]*Action, error)
|
||||||
|
SelectUserByUsername(username string) (*User, error)
|
||||||
|
InsertUser(user *User) (int, error)
|
||||||
|
SelectCurrentPlan(userID int) (*CurrentPlan, error)
|
||||||
|
InsertCurrentPlan(currentPlan *CurrentPlan, userID int) error
|
||||||
|
UpdateCurrentPlan(currentPlan *CurrentPlan, userID int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Model represents a current model item.
|
// Model represents a current model item.
|
||||||
@@ -17,3 +32,12 @@ type Model struct {
|
|||||||
func New(store Store) *Model {
|
func New(store Store) *Model {
|
||||||
return &Model{Store: store}
|
return &Model{Store: store}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Healthy returns an error if the connection is healthy.
|
||||||
|
// Wrapper over db.Ping()
|
||||||
|
func (m *Model) Healthy() error {
|
||||||
|
if m.Store == nil {
|
||||||
|
return fmt.Errorf("No store available")
|
||||||
|
}
|
||||||
|
return m.ConnectionLive()
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,54 +2,90 @@ package models_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"gitea.deepak.science/deepak/gogmagog/models"
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/store"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type multiStore struct {
|
|
||||||
actions []*models.Action
|
|
||||||
plans []*models.Plan
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *multiStore) SelectActions() ([]*models.Action, error) {
|
|
||||||
return ms.actions, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *multiStore) SelectActionByID(id int) (*models.Action, error) {
|
|
||||||
return ms.actions[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *multiStore) SelectPlans() ([]*models.Plan, error) {
|
|
||||||
return ms.plans, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ms *multiStore) SelectPlanByID(id int) (*models.Plan, error) {
|
|
||||||
return ms.plans[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestModelActions(t *testing.T) {
|
func TestModelActions(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
a1 := &models.Action{ActionID: 3}
|
a1 := &models.Action{ActionID: 3}
|
||||||
a2 := &models.Action{ActionID: 4}
|
a2 := &models.Action{ActionID: 4}
|
||||||
|
userID := 3
|
||||||
p := &models.Plan{PlanID: 6}
|
p := &models.Plan{PlanID: 6}
|
||||||
ss := &multiStore{
|
|
||||||
[]*models.Action{a1, a2},
|
|
||||||
[]*models.Plan{p}}
|
|
||||||
m := models.New(ss)
|
|
||||||
|
|
||||||
actions, err := m.Actions()
|
str, _ := store.GetInMemoryStore()
|
||||||
|
str.InsertAction(a1, userID)
|
||||||
|
str.InsertPlan(p, userID)
|
||||||
|
m := models.New(str)
|
||||||
|
|
||||||
|
actions, err := m.Actions(userID)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(2, len(actions))
|
assert.Equal(1, len(actions))
|
||||||
|
|
||||||
plans, err := m.Plans()
|
firstAction, err := m.Action(1, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(1, firstAction.ActionID)
|
||||||
|
|
||||||
|
actionID, err := m.AddAction(a2, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(2, actionID)
|
||||||
|
|
||||||
|
err = m.SaveAction(a1, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModelPlanMethods(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
userID := 3
|
||||||
|
a1 := &models.Action{ActionID: 3, PlanID: 1}
|
||||||
|
a2 := &models.Action{ActionID: 4}
|
||||||
|
p := &models.Plan{}
|
||||||
|
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
str.InsertPlan(p, userID)
|
||||||
|
str.InsertAction(a1, userID)
|
||||||
|
str.InsertAction(a2, userID)
|
||||||
|
m := models.New(str)
|
||||||
|
|
||||||
|
plans, err := m.Plans(userID)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(1, len(plans))
|
assert.Equal(1, len(plans))
|
||||||
|
|
||||||
firstAction, err := m.Action(3)
|
firstPlan, err := m.Plan(1, userID)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.EqualValues(3, firstAction.ActionID)
|
assert.EqualValues(1, firstPlan.PlanID)
|
||||||
|
|
||||||
firstPlan, err := m.Plan(6)
|
p2 := &models.Plan{PlanDescription: "testing", PlanID: 1}
|
||||||
|
m.SavePlan(p2, userID)
|
||||||
|
p2, err = m.Plan(1, userID)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.EqualValues(6, firstPlan.PlanID)
|
assert.Equal("testing", p2.PlanDescription)
|
||||||
|
|
||||||
|
actions, err := m.GetActions(firstPlan, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(1, len(actions))
|
||||||
|
|
||||||
|
planId, err := m.AddPlan(&models.Plan{}, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(2, planId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestModelHealthy(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
m := models.New(str)
|
||||||
|
|
||||||
|
err := m.Healthy()
|
||||||
|
assert.Nil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNilModelUnhealthy(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := models.New(nil)
|
||||||
|
|
||||||
|
err := m.Healthy()
|
||||||
|
assert.NotNil(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,34 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Plan represents a single day's agenda of actions.
|
// Plan represents a single day's agenda of actions.
|
||||||
type Plan struct {
|
type Plan struct {
|
||||||
PlanID int64
|
PlanID int64 `json:"plan_id"`
|
||||||
PlanDate time.Time
|
PlanDescription string `json:"plan_description"`
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plans returns all plans in the model.
|
// Plans returns all plans in the model.
|
||||||
func (m *Model) Plans() ([]*Plan, error) {
|
func (m *Model) Plans(userID int) ([]*Plan, error) {
|
||||||
return m.SelectPlans()
|
return m.SelectPlans(userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plan returns a single plan from the store by plan_id.
|
// Plan returns a single plan from the store by plan_id.
|
||||||
func (m *Model) Plan(id int) (*Plan, error) {
|
func (m *Model) Plan(id int, userID int) (*Plan, error) {
|
||||||
return m.SelectPlanByID(id)
|
plan, err := m.SelectPlanByID(id, userID)
|
||||||
|
return plan, wrapNotFound(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPlan inserts a given plan into the store, returning the generated PlanID. The provided PlanID is ignored.
|
||||||
|
func (m *Model) AddPlan(plan *Plan, userID int) (int, error) {
|
||||||
|
return m.InsertPlan(plan, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SavePlan saves and updates a plan.
|
||||||
|
func (m *Model) SavePlan(plan *Plan, userID int) error {
|
||||||
|
return m.UpdatePlan(plan, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetActions returns the actions associated with a particular plan.
|
||||||
|
func (m *Model) GetActions(plan *Plan, userID int) ([]*Action, error) {
|
||||||
|
return m.SelectActionsByPlanID(plan, userID)
|
||||||
}
|
}
|
||||||
|
|||||||
96
models/user.go
Normal file
96
models/user.go
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User represents the full DB user field, for inserts and compares.
|
||||||
|
// No reason to return the hashed pw on the route though.
|
||||||
|
type User struct {
|
||||||
|
UserID int64
|
||||||
|
Username string
|
||||||
|
DisplayName string
|
||||||
|
Password []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserNoPassword contains the non password user fields.
|
||||||
|
// This is preferred outside of the model / store.
|
||||||
|
type UserNoPassword struct {
|
||||||
|
UserID int64 `json:"user_id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyUserByUsernamePassword returns a single user by the unique username, if the provided password is correct.
|
||||||
|
func (m *Model) VerifyUserByUsernamePassword(username string, password string) (*UserNoPassword, error) {
|
||||||
|
user, err := m.SelectUserByUsername(username)
|
||||||
|
if err != nil {
|
||||||
|
// throwaway to pad time
|
||||||
|
hashPassword(username)
|
||||||
|
return nil, wrapInvalidLogin(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bcrypt.CompareHashAndPassword(user.Password, []byte(password))
|
||||||
|
if err != nil {
|
||||||
|
return nil, wrapInvalidLogin(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return user.NoPassword(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NoPassword strips the user of password.
|
||||||
|
func (u *User) NoPassword() *UserNoPassword {
|
||||||
|
return &UserNoPassword{
|
||||||
|
UserID: u.UserID,
|
||||||
|
Username: u.Username,
|
||||||
|
DisplayName: u.DisplayName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUserRequest represents a desired user creation.
|
||||||
|
type CreateUserRequest struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
DisplayName string `json:"display_name"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateUser takes in a create user request and returns the ID of the newly created user.
|
||||||
|
func (m *Model) CreateUser(req *CreateUserRequest) (int, error) {
|
||||||
|
if req.Username == "" {
|
||||||
|
return -1, fmt.Errorf("No username provided")
|
||||||
|
}
|
||||||
|
if req.Password == "" {
|
||||||
|
return -1, fmt.Errorf("No password provided")
|
||||||
|
}
|
||||||
|
hash, err := hashPassword(req.Password)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
desiredUser := &User{
|
||||||
|
Username: req.Username,
|
||||||
|
DisplayName: req.DisplayName,
|
||||||
|
Password: hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.InsertUser(desiredUser)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserByUsername retrieves a single username from the store, verifying the passed in userID.
|
||||||
|
func (m *Model) UserByUsername(username string, userID int) (*UserNoPassword, error) {
|
||||||
|
user, err := m.SelectUserByUsername(username)
|
||||||
|
if user == nil {
|
||||||
|
return nil, wrapNotFound(err)
|
||||||
|
}
|
||||||
|
if int(user.UserID) != userID {
|
||||||
|
return nil, ¬FoundError{error: fmt.Errorf("provided userID does not match the retrieved user")}
|
||||||
|
}
|
||||||
|
return user.NoPassword(), wrapNotFound(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashPassword hashes a password
|
||||||
|
func hashPassword(password string) ([]byte, error) {
|
||||||
|
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 11)
|
||||||
|
return bytes, err
|
||||||
|
}
|
||||||
104
models/user_test.go
Normal file
104
models/user_test.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package models_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/store"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestModelUsers(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
a1 := &models.Action{ActionID: 3}
|
||||||
|
a2 := &models.Action{ActionID: 4}
|
||||||
|
p := &models.Plan{PlanID: 6}
|
||||||
|
|
||||||
|
username := "test1"
|
||||||
|
// password := password
|
||||||
|
user1 := &models.User{Username: username, DisplayName: "Ted Est", Password: []byte("$2y$05$6SVV35GX4cB4PDPhRaDD/exsL.HV8QtMMr60YL6dLyqtX4l58q.cy")}
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
str.InsertPlan(p, 3)
|
||||||
|
str.InsertAction(a1, 3)
|
||||||
|
str.InsertAction(a2, 3)
|
||||||
|
str.InsertUser(user1)
|
||||||
|
m := models.New(str)
|
||||||
|
|
||||||
|
userNoPass, err := m.UserByUsername("test1", 1)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.NotNil(userNoPass)
|
||||||
|
|
||||||
|
userNoPass, err = m.UserByUsername("test1", 2)
|
||||||
|
assert.NotNil(err)
|
||||||
|
assert.True(models.IsNotFoundError(err))
|
||||||
|
assert.Nil(userNoPass)
|
||||||
|
|
||||||
|
userNoPass, err = m.UserByUsername("test2", 2)
|
||||||
|
assert.NotNil(err)
|
||||||
|
assert.True(models.IsNotFoundError(err))
|
||||||
|
assert.Nil(userNoPass)
|
||||||
|
|
||||||
|
user, err := m.VerifyUserByUsernamePassword("test1", "password")
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.NotNil(user)
|
||||||
|
|
||||||
|
user, err = m.VerifyUserByUsernamePassword("test1", "wrong_password")
|
||||||
|
assert.NotNil(err)
|
||||||
|
assert.Nil(user)
|
||||||
|
|
||||||
|
user, err = m.VerifyUserByUsernamePassword("test2", "password")
|
||||||
|
assert.NotNil(err)
|
||||||
|
assert.Nil(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorUsers(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getErrorModel(fmt.Errorf("err"))
|
||||||
|
|
||||||
|
user, err := m.VerifyUserByUsernamePassword("snth", "aoeu")
|
||||||
|
assert.Nil(user)
|
||||||
|
assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateUser(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
username := "test"
|
||||||
|
displayName := "Ted Est"
|
||||||
|
pass := "abc"
|
||||||
|
u := &models.CreateUserRequest{Username: username, DisplayName: displayName, Password: pass}
|
||||||
|
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
m := models.New(str)
|
||||||
|
|
||||||
|
id, err := m.CreateUser(u)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(1, id)
|
||||||
|
}
|
||||||
|
func TestCreateUserFailValidation(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
username := ""
|
||||||
|
displayName := "Ted Est"
|
||||||
|
pass := "abc"
|
||||||
|
u := &models.CreateUserRequest{Username: username, DisplayName: displayName, Password: pass}
|
||||||
|
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
m := models.New(str)
|
||||||
|
|
||||||
|
_, err := m.CreateUser(u)
|
||||||
|
assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateUserFailValidationPassword(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
username := "aoeu"
|
||||||
|
displayName := "Ted Est"
|
||||||
|
pass := ""
|
||||||
|
u := &models.CreateUserRequest{Username: username, DisplayName: displayName, Password: pass}
|
||||||
|
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
m := models.New(str)
|
||||||
|
|
||||||
|
_, err := m.CreateUser(u)
|
||||||
|
assert.NotNil(err)
|
||||||
|
}
|
||||||
215
routes/actions.go
Normal file
215
routes/actions.go
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewActionRouter returns a new action router
|
||||||
|
func NewActionRouter(m *models.Model) http.Handler {
|
||||||
|
router := chi.NewRouter()
|
||||||
|
router.Get("/", getActionsFunc(m))
|
||||||
|
router.Post("/", postActionFunc(m))
|
||||||
|
router.Get("/{actionid}", getActionByIDFunc(m))
|
||||||
|
router.Put("/{actionid}", putActionFunc(m))
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
|
func getActionsFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
userID, userErr := tokens.GetUserID(r.Context())
|
||||||
|
if userErr != nil {
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
actions []*models.Action
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
planIDString := r.URL.Query().Get("plan_id")
|
||||||
|
if planIDString == "" {
|
||||||
|
actions, err = m.Actions(userID)
|
||||||
|
} else {
|
||||||
|
planID, convErr := strconv.ParseInt(planIDString, 10, 64)
|
||||||
|
if convErr != nil {
|
||||||
|
actions = []*models.Action{}
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
|
plan := &models.Plan{PlanID: planID}
|
||||||
|
actions, err = m.GetActions(plan, userID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(actions); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getActionByIDFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
userID, userErr := tokens.GetUserID(r.Context())
|
||||||
|
if userErr != nil {
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := strconv.Atoi(chi.URLParam(r, "actionid"))
|
||||||
|
if err != nil {
|
||||||
|
notFoundHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
action, err := m.Action(id, userID)
|
||||||
|
if err != nil {
|
||||||
|
if models.IsNotFoundError(err) {
|
||||||
|
notFoundHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(action); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type createActionResponse struct {
|
||||||
|
CreatedAction *models.Action `json:"created_action"`
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func postActionFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
userID, err := tokens.GetUserID(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, 1024)
|
||||||
|
dec := json.NewDecoder(r.Body)
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
var a models.Action
|
||||||
|
err = dec.Decode(&a)
|
||||||
|
if err != nil {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = dec.Decode(&struct{}{})
|
||||||
|
if err != io.EOF {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
action := &models.Action{
|
||||||
|
ActionDescription: a.ActionDescription,
|
||||||
|
EstimatedChunks: a.EstimatedChunks,
|
||||||
|
CompletedChunks: a.CompletedChunks,
|
||||||
|
CompletedOn: a.CompletedOn,
|
||||||
|
PlanID: a.PlanID,
|
||||||
|
}
|
||||||
|
id, err := m.AddAction(action, userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
action, err = m.Action(id, userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &createActionResponse{
|
||||||
|
CreatedAction: action,
|
||||||
|
ID: int64(id),
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type updateActionResponse struct {
|
||||||
|
UpdatedAction *models.Action `json:"updated_action"`
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func putActionFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
userID, err := tokens.GetUserID(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := strconv.Atoi(chi.URLParam(r, "actionid"))
|
||||||
|
if err != nil {
|
||||||
|
notFoundHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, 1024)
|
||||||
|
dec := json.NewDecoder(r.Body)
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
var a models.Action
|
||||||
|
err = dec.Decode(&a)
|
||||||
|
if err != nil {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = dec.Decode(&struct{}{})
|
||||||
|
if err != io.EOF {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
action := &models.Action{
|
||||||
|
ActionDescription: a.ActionDescription,
|
||||||
|
EstimatedChunks: a.EstimatedChunks,
|
||||||
|
CompletedChunks: a.CompletedChunks,
|
||||||
|
CompletedOn: a.CompletedOn,
|
||||||
|
PlanID: a.PlanID,
|
||||||
|
ActionID: int64(id),
|
||||||
|
}
|
||||||
|
err = m.SaveAction(action, userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
action, err = m.Action(id, userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &updateActionResponse{
|
||||||
|
UpdatedAction: action,
|
||||||
|
ID: int64(id),
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
297
routes/actions_test.go
Normal file
297
routes/actions_test.go
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmptyActions(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
expected := `[]`
|
||||||
|
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOneAction(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
createdDate, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
updatedDate, _ := time.Parse("2006-01-02", "2021-01-02")
|
||||||
|
completedDate, _ := time.Parse("2006-01-02", "2021-01-03")
|
||||||
|
a1 := &models.Action{ActionID: 3, ActionDescription: "testing", CompletedChunks: 1, CompletedOn: &completedDate, CreatedAt: &createdDate, UpdatedAt: &updatedDate, EstimatedChunks: 3, PlanID: 0}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddAction(a1, 3)
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `[
|
||||||
|
{
|
||||||
|
"action_id": 1,
|
||||||
|
"action_description": "testing",
|
||||||
|
"user_id": 3,
|
||||||
|
"estimated_chunks": 3,
|
||||||
|
"completed_chunks": 1,
|
||||||
|
"completed_on": "2021-01-03T00:00:00Z",
|
||||||
|
"updated_at": "2021-01-02T00:00:00Z",
|
||||||
|
"created_at": "2021-01-01T00:00:00Z",
|
||||||
|
"plan_id": 0
|
||||||
|
}
|
||||||
|
]`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorAction(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyActionErrorWriter(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOneActionByID(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
createdDate, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
updatedDate, _ := time.Parse("2006-01-02", "2021-01-02")
|
||||||
|
a := &models.Action{ActionID: 6, ActionDescription: "howdy", CompletedOn: nil, CreatedAt: &createdDate, UpdatedAt: &updatedDate, CompletedChunks: 0, EstimatedChunks: 54, PlanID: 3}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.InsertAction(a, 3)
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/1", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `{
|
||||||
|
"action_id": 1,
|
||||||
|
"action_description": "howdy",
|
||||||
|
"user_id": 3,
|
||||||
|
"estimated_chunks": 54,
|
||||||
|
"completed_chunks": 0,
|
||||||
|
"updated_at": "2021-01-02T00:00:00Z",
|
||||||
|
"created_at": "2021-01-01T00:00:00Z",
|
||||||
|
"plan_id": 3
|
||||||
|
}`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorActionByID(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/5", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyActionErrorWriterByID(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
a := &models.Action{ActionID: 6}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddAction(a, 3)
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/1", nil)
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNotFoundActionByIDText(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/wo", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusNotFound, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
func TestNotFoundActionByIDEmpty(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/1", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusNotFound, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestActionsByPlanID(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
createdDate, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
updatedDate, _ := time.Parse("2006-01-02", "2021-01-02")
|
||||||
|
a := &models.Action{ActionID: 1, ActionDescription: "howdy", CompletedOn: nil, CreatedAt: &createdDate, UpdatedAt: &updatedDate, CompletedChunks: 0, EstimatedChunks: 54, PlanID: 6}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddAction(a, 3)
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/?plan_id=6", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `[
|
||||||
|
{
|
||||||
|
"action_id": 1,
|
||||||
|
"action_description": "howdy",
|
||||||
|
"user_id": 3,
|
||||||
|
"estimated_chunks": 54,
|
||||||
|
"completed_chunks": 0,
|
||||||
|
"updated_at": "2021-01-02T00:00:00Z",
|
||||||
|
"created_at": "2021-01-01T00:00:00Z",
|
||||||
|
"plan_id": 6
|
||||||
|
}
|
||||||
|
]`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestActionsByPlanIDInvalidID(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
createdDate, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
updatedDate, _ := time.Parse("2006-01-02", "2021-01-02")
|
||||||
|
a := &models.Action{ActionID: 6, ActionDescription: "howdy", CompletedOn: nil, CreatedAt: &createdDate, UpdatedAt: &updatedDate, CompletedChunks: 0, EstimatedChunks: 54, PlanID: 3}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddAction(a, 3)
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/?plan_id=aoeu", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `[]`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
119
routes/actions_unauthorized_test.go
Normal file
119
routes/actions_unauthorized_test.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmptyActionEmptyContext(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOneActionEmptyContext(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2006-01-02"
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription, UserID: 3}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOneActionByIDEmptyContext(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription, UserID: 3}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/1", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPureJSONActionEmptyContext(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_description": "2021-01-01T00:00:00Z",
|
||||||
|
"plan_id": 1,
|
||||||
|
"user_id": 3
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPutActionEmptyContext(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
a := &models.Action{PlanID: 6}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddAction(a, 3)
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data, _ := json.Marshal(a)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
|
||||||
|
}
|
||||||
103
routes/auth.go
Normal file
103
routes/auth.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewAuthRouter returns a new auth router.
|
||||||
|
func NewAuthRouter(m *models.Model, tok tokens.Toker) http.Handler {
|
||||||
|
router := chi.NewRouter()
|
||||||
|
router.Post("/register", postUserFunc(m))
|
||||||
|
router.Post("/tokens", createTokenFunc(m, tok))
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
|
type createUserResponse struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func postUserFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, 1024)
|
||||||
|
dec := json.NewDecoder(r.Body)
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
var req models.CreateUserRequest
|
||||||
|
err := dec.Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = dec.Decode(&struct{}{})
|
||||||
|
if err != io.EOF {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = m.CreateUser(&req)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &createUserResponse{
|
||||||
|
Username: req.Username,
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type loginCreds struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
type createdToken struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func createTokenFunc(m *models.Model, tok tokens.Toker) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, 1024)
|
||||||
|
dec := json.NewDecoder(r.Body)
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
var creds loginCreds
|
||||||
|
err := dec.Decode(&creds)
|
||||||
|
if err != nil {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = dec.Decode(&struct{}{})
|
||||||
|
if err != io.EOF {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := m.VerifyUserByUsernamePassword(creds.Username, creds.Password)
|
||||||
|
if err != nil {
|
||||||
|
if models.IsInvalidLoginError(err) {
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
response := &createdToken{Token: tok.EncodeUser(user)}
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
202
routes/auth_login_test.go
Normal file
202
routes/auth_login_test.go
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoginAuth(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getEmptyModel()
|
||||||
|
username := "testing_username"
|
||||||
|
displayName := "testing_name"
|
||||||
|
password := "pass"
|
||||||
|
m.CreateUser(&models.CreateUserRequest{Username: username, DisplayName: displayName, Password: password})
|
||||||
|
|
||||||
|
toker := tokens.GetDeterministicToker()
|
||||||
|
data := []byte(`{
|
||||||
|
"username": "testing_username",
|
||||||
|
"password": "pass"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequest("POST", "/tokens", bytes.NewBuffer(data))
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router := routes.NewAuthRouter(m, toker)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
expected := `{
|
||||||
|
"token": "{\"ID\":1,\"Username\":\"testing_username\"}"
|
||||||
|
}`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoginBadCreds(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
toker := tokens.GetDeterministicToker()
|
||||||
|
data := []byte(`{
|
||||||
|
"username": "testing_use
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequest("POST", "/tokens", bytes.NewBuffer(data))
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router := routes.NewAuthRouter(m, toker)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoginBadRequestTwoBodies(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
toker := tokens.GetDeterministicToker()
|
||||||
|
data := []byte(`{
|
||||||
|
"username": "testing_username",
|
||||||
|
"password": "pass"
|
||||||
|
}{
|
||||||
|
"username": "testing_username",
|
||||||
|
"password": "pass"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequest("POST", "/tokens", bytes.NewBuffer(data))
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router := routes.NewAuthRouter(m, toker)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoginAuthWrongPass(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getEmptyModel()
|
||||||
|
username := "testing_username"
|
||||||
|
displayName := "testing_name"
|
||||||
|
password := "pass"
|
||||||
|
m.CreateUser(&models.CreateUserRequest{Username: username, DisplayName: displayName, Password: password})
|
||||||
|
|
||||||
|
toker := tokens.GetDeterministicToker()
|
||||||
|
data := []byte(`{
|
||||||
|
"username": "testing_username",
|
||||||
|
"password": "badpass"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequest("POST", "/tokens", bytes.NewBuffer(data))
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router := routes.NewAuthRouter(m, toker)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoginErrorModel(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorModel("error")
|
||||||
|
|
||||||
|
toker := tokens.GetDeterministicToker()
|
||||||
|
data := []byte(`{
|
||||||
|
"username": "testing_username",
|
||||||
|
"password": "badpass"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequest("POST", "/tokens", bytes.NewBuffer(data))
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router := routes.NewAuthRouter(m, toker)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoginBadWriter(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getEmptyModel()
|
||||||
|
username := "testing_username"
|
||||||
|
displayName := "testing_name"
|
||||||
|
password := "pass"
|
||||||
|
m.CreateUser(&models.CreateUserRequest{Username: username, DisplayName: displayName, Password: password})
|
||||||
|
|
||||||
|
toker := tokens.GetDeterministicToker()
|
||||||
|
data := []byte(`{
|
||||||
|
"username": "testing_username",
|
||||||
|
"password": "pass"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequest("POST", "/tokens", bytes.NewBuffer(data))
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router := routes.NewAuthRouter(m, toker)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// func TestRegisterBadWriter(t *testing.T) {
|
||||||
|
// // set up
|
||||||
|
// assert := assert.New(t)
|
||||||
|
// m := getEmptyModel()
|
||||||
|
// toker := tokens.New("secret")
|
||||||
|
// data := []byte(`{
|
||||||
|
// "username": "test",
|
||||||
|
// "password": "pass",
|
||||||
|
// "display_name": "My Display Name"
|
||||||
|
// }`)
|
||||||
|
// req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(data))
|
||||||
|
//
|
||||||
|
// rr := NewBadWriter()
|
||||||
|
//
|
||||||
|
// // function under test
|
||||||
|
// router := routes.NewAuthRouter(m, toker)
|
||||||
|
// router.ServeHTTP(rr, req)
|
||||||
|
//
|
||||||
|
// // check results
|
||||||
|
// status := rr.Code
|
||||||
|
// assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
//
|
||||||
|
// }
|
||||||
139
routes/auth_register_test.go
Normal file
139
routes/auth_register_test.go
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRegisterAuth(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
data := []byte(`{
|
||||||
|
"username": "test",
|
||||||
|
"password": "pass",
|
||||||
|
"display_name": "My Display Name"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(data))
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router := routes.NewAuthRouter(m, toker)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusCreated, status)
|
||||||
|
expected := `{
|
||||||
|
"username": "test"
|
||||||
|
}`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterBadRequestAuth(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
data := []byte(`{
|
||||||
|
"username": y Display Name"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(data))
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router := routes.NewAuthRouter(m, toker)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterBadRequestTwoBodies(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
data := []byte(`{
|
||||||
|
"username": "test",
|
||||||
|
"password": "pass",
|
||||||
|
"display_name": "My Display Name"
|
||||||
|
}, {
|
||||||
|
"username": "test",
|
||||||
|
"password": "pass",
|
||||||
|
"display_name": "My Display Name"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(data))
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router := routes.NewAuthRouter(m, toker)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterErrorModel(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getErrorModel("here's an error")
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
data := []byte(`{
|
||||||
|
"username": "test",
|
||||||
|
"password": "pass",
|
||||||
|
"display_name": "My Display Name"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(data))
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router := routes.NewAuthRouter(m, toker)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRegisterBadWriter(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
data := []byte(`{
|
||||||
|
"username": "test",
|
||||||
|
"password": "pass",
|
||||||
|
"display_name": "My Display Name"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequest("POST", "/register", bytes.NewBuffer(data))
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router := routes.NewAuthRouter(m, toker)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
48
routes/currentUser.go
Normal file
48
routes/currentUser.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewCurrentUserRouter returns a new router for getting the current user.
|
||||||
|
func NewCurrentUserRouter(m *models.Model) http.Handler {
|
||||||
|
router := chi.NewRouter()
|
||||||
|
router.Get("/", getMeFunc(m))
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMeFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
userID, err := tokens.GetUserID(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
username, err := tokens.GetUsername(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
log.Print(err)
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := m.UserByUsername(username, userID)
|
||||||
|
if err != nil {
|
||||||
|
if models.IsNotFoundError(err) {
|
||||||
|
notFoundHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(user); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
158
routes/currentUser_test.go
Normal file
158
routes/currentUser_test.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmptyCurrentUser(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewCurrentUserRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(tokens.GetContextForUserValues(3, "testing"), "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusNotFound, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSingleUser(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
idToUse := 1
|
||||||
|
username := "testing_username"
|
||||||
|
displayName := "testing_name"
|
||||||
|
password := "pass"
|
||||||
|
m.CreateUser(&models.CreateUserRequest{Username: username, DisplayName: displayName, Password: password})
|
||||||
|
|
||||||
|
router := routes.NewCurrentUserRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(tokens.GetContextForUserValues(idToUse, username), "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
expected := `{
|
||||||
|
"user_id": 1,
|
||||||
|
"username": "testing_username",
|
||||||
|
"display_name": "testing_name"
|
||||||
|
}`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSingleUserEmptyContext(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
username := "testing_username"
|
||||||
|
displayName := "testing_name"
|
||||||
|
password := "pass"
|
||||||
|
m.CreateUser(&models.CreateUserRequest{Username: username, DisplayName: displayName, Password: password})
|
||||||
|
|
||||||
|
router := routes.NewCurrentUserRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSingleUserContextNoUserID(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
idToUse := 1
|
||||||
|
username := "testing_username"
|
||||||
|
displayName := "testing_name"
|
||||||
|
password := "pass"
|
||||||
|
m.CreateUser(&models.CreateUserRequest{Username: username, DisplayName: displayName, Password: password})
|
||||||
|
|
||||||
|
router := routes.NewCurrentUserRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(tokens.SetUserID(context.Background(), idToUse), "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorUserContextNoUserID(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getErrorModel("Here's an error.")
|
||||||
|
|
||||||
|
idToUse := 1
|
||||||
|
|
||||||
|
router := routes.NewCurrentUserRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(tokens.GetContextForUserValues(idToUse, "username"), "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSingleUserErrorWriter(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
idToUse := 1
|
||||||
|
username := "testing_username"
|
||||||
|
displayName := "testing_name"
|
||||||
|
password := "pass"
|
||||||
|
m.CreateUser(&models.CreateUserRequest{Username: username, DisplayName: displayName, Password: password})
|
||||||
|
|
||||||
|
router := routes.NewCurrentUserRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(tokens.GetContextForUserValues(idToUse, username), "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
160
routes/current_plan.go
Normal file
160
routes/current_plan.go
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewCurrentPlanRouter returns a new primary plan router
|
||||||
|
func NewCurrentPlanRouter(m *models.Model) http.Handler {
|
||||||
|
router := chi.NewRouter()
|
||||||
|
router.Get("/", getCurrentPlanFunc(m))
|
||||||
|
router.Post("/", postCurrentPlanFunc(m))
|
||||||
|
router.Put("/", putCurrentPlanFunc(m))
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCurrentPlanFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
userID, userErr := tokens.GetUserID(r.Context())
|
||||||
|
if userErr != nil {
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pp, err := m.CurrentPlan(userID)
|
||||||
|
if err != nil {
|
||||||
|
if models.IsNotFoundError(err) {
|
||||||
|
notFoundHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(pp); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type createCurrentPlanResponse struct {
|
||||||
|
CreatedCurrentPlan *models.CurrentPlan `json:"created_current_plan"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func postCurrentPlanFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
userID, err := tokens.GetUserID(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, 1024)
|
||||||
|
dec := json.NewDecoder(r.Body)
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
var pp models.CurrentPlan
|
||||||
|
err = dec.Decode(&pp)
|
||||||
|
if err != nil {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = dec.Decode(&struct{}{})
|
||||||
|
if err != io.EOF {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newPP := &models.CurrentPlan{
|
||||||
|
PlanID: pp.PlanID,
|
||||||
|
UserID: int64(userID),
|
||||||
|
}
|
||||||
|
err = m.AddCurrentPlan(newPP, userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
finishedPP, err := m.CurrentPlan(userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &createCurrentPlanResponse{
|
||||||
|
CreatedCurrentPlan: finishedPP,
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type updateCurrentPlanResponse struct {
|
||||||
|
UpdatedCurrentPlan *models.CurrentPlan `json:"updated_current_plan"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func putCurrentPlanFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
userID, err := tokens.GetUserID(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = m.CurrentPlan(userID)
|
||||||
|
if models.IsNotFoundError(err) {
|
||||||
|
notFoundHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, 1024)
|
||||||
|
dec := json.NewDecoder(r.Body)
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
var pp models.CurrentPlan
|
||||||
|
err = dec.Decode(&pp)
|
||||||
|
if err != nil {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = dec.Decode(&struct{}{})
|
||||||
|
if err != io.EOF {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newPP := &models.CurrentPlan{
|
||||||
|
PlanID: pp.PlanID,
|
||||||
|
UserID: int64(userID),
|
||||||
|
}
|
||||||
|
err = m.SaveCurrentPlan(newPP, userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newPP, err = m.CurrentPlan(userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &updateCurrentPlanResponse{
|
||||||
|
UpdatedCurrentPlan: newPP,
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
228
routes/current_plan_post_test.go
Normal file
228
routes/current_plan_post_test.go
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPostSinglePlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
|
||||||
|
pp := &models.CurrentPlan{PlanID: 1}
|
||||||
|
data, _ := json.Marshal(pp)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusCreated, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPostCurrentPlanUnauth(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
|
||||||
|
pp := &models.CurrentPlan{PlanID: 1}
|
||||||
|
data, _ := json.Marshal(pp)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtraFieldJSONCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5,
|
||||||
|
"sabotage": "omg"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyBodyJSONCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
|
||||||
|
data := []byte(``)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwoBodyCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}, {
|
||||||
|
"plan_id": 7
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorCreateCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getErrorModel("error model")
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorOnRetrieveCreateCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getErrorOnGetModel("error model")
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorWriterCreateCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
}
|
||||||
269
routes/current_plan_put_test.go
Normal file
269
routes/current_plan_put_test.go
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPutSinglePlanNotFound(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
|
||||||
|
pp := &models.CurrentPlan{PlanID: 1}
|
||||||
|
data, _ := json.Marshal(pp)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusNotFound, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPutSinglePlanNonDup(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
|
||||||
|
|
||||||
|
pp := &models.CurrentPlan{PlanID: 2}
|
||||||
|
data, _ := json.Marshal(pp)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPutCurrentPlanUnauth(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
|
||||||
|
|
||||||
|
pp := &models.CurrentPlan{PlanID: 1}
|
||||||
|
data, _ := json.Marshal(pp)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "PUT", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtraFieldJSONPutCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
|
||||||
|
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5,
|
||||||
|
"sabotage": "omg"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyBodyJSONPutCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
|
||||||
|
|
||||||
|
data := []byte(``)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwoBodyPutCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
|
||||||
|
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}, {
|
||||||
|
"plan_id": 7
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorPutCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getErrorModel("error model")
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
|
||||||
|
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorOnRetrievePutCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getErrorOnGetModel("error model")
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
|
||||||
|
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorWriterPutCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
plan := &models.Plan{}
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddPlan(plan, 3)
|
||||||
|
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
|
||||||
|
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
}
|
||||||
117
routes/current_plan_test.go
Normal file
117
routes/current_plan_test.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
// "gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmptyCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusNotFound, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyCurrentPlanUnauth(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyCurrentPlanErrorWriter(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 3}, 3)
|
||||||
|
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSingleCurrentPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 3}, 3)
|
||||||
|
|
||||||
|
router := routes.NewCurrentPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `{
|
||||||
|
"plan_id": 3,
|
||||||
|
"user_id": 3
|
||||||
|
}`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
32
routes/errors.go
Normal file
32
routes/errors.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func serverError(w http.ResponseWriter, err error) {
|
||||||
|
code := http.StatusInternalServerError
|
||||||
|
log.Printf("received error: {%v}", err)
|
||||||
|
http.Error(w, http.StatusText(code), code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func badRequestError(w http.ResponseWriter, err error) {
|
||||||
|
code := http.StatusBadRequest
|
||||||
|
log.Printf("received error: {%v}", err)
|
||||||
|
http.Error(w, http.StatusText(code), code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
code := http.StatusMethodNotAllowed
|
||||||
|
http.Error(w, http.StatusText(code), code)
|
||||||
|
}
|
||||||
|
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
code := http.StatusNotFound
|
||||||
|
http.Error(w, http.StatusText(code), code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unauthorizedHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
code := http.StatusUnauthorized
|
||||||
|
http.Error(w, http.StatusText(code), code)
|
||||||
|
}
|
||||||
58
routes/health.go
Normal file
58
routes/health.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newHealthRouter(m *models.Model) http.Handler {
|
||||||
|
router := chi.NewRouter()
|
||||||
|
router.Get("/", getHealthFunc(m))
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
|
type healthCheck struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Healthy bool `json:"healthy"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHealthFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var healths []*healthCheck
|
||||||
|
|
||||||
|
healths = append(healths, dbHealth(m))
|
||||||
|
|
||||||
|
code := http.StatusOK
|
||||||
|
for _, h := range healths {
|
||||||
|
if !h.Healthy {
|
||||||
|
code = http.StatusInternalServerError
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(code)
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(healths); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbHealth(m *models.Model) *healthCheck {
|
||||||
|
errMessage := ""
|
||||||
|
health := true
|
||||||
|
name := "Store"
|
||||||
|
err := m.Healthy()
|
||||||
|
if err != nil {
|
||||||
|
errMessage = err.Error()
|
||||||
|
health = false
|
||||||
|
}
|
||||||
|
return &healthCheck{
|
||||||
|
Name: name,
|
||||||
|
Healthy: health,
|
||||||
|
Message: errMessage,
|
||||||
|
}
|
||||||
|
}
|
||||||
88
routes/health_test.go
Normal file
88
routes/health_test.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmptyHeatlhErrorWriter(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
router := routes.NewRouter(m, tokens.New("whatever"))
|
||||||
|
req, _ := http.NewRequest("GET", "/health", nil)
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyHealth(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewRouter(m, tokens.New("whatever"))
|
||||||
|
req, _ := http.NewRequest("GET", "/health", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
expected := `[
|
||||||
|
{
|
||||||
|
"name": "Store",
|
||||||
|
"healthy": true,
|
||||||
|
"message": ""
|
||||||
|
}
|
||||||
|
]`
|
||||||
|
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnhealthyDB(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
errorMsg := "error"
|
||||||
|
m := getErrorModel(errorMsg)
|
||||||
|
router := routes.NewRouter(m, tokens.New("whatever"))
|
||||||
|
req, _ := http.NewRequest("GET", "/health", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
expected := fmt.Sprintf(`[
|
||||||
|
{
|
||||||
|
"name": "Store",
|
||||||
|
"healthy": false,
|
||||||
|
"message": "%s"
|
||||||
|
}
|
||||||
|
]`, errorMsg)
|
||||||
|
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
29
routes/http_util_test.go
Normal file
29
routes/http_util_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BadResponseWriter struct {
|
||||||
|
Code int
|
||||||
|
header http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBadWriter() *BadResponseWriter {
|
||||||
|
return &BadResponseWriter{
|
||||||
|
header: http.Header{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *BadResponseWriter) Header() http.Header {
|
||||||
|
return w.header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *BadResponseWriter) Write(b []byte) (int, error) {
|
||||||
|
return 0, fmt.Errorf("always an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *BadResponseWriter) WriteHeader(statusCode int) {
|
||||||
|
w.Code = statusCode
|
||||||
|
}
|
||||||
218
routes/plan_put_test.go
Normal file
218
routes/plan_put_test.go
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPureJSONPutPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
p := &models.Plan{
|
||||||
|
PlanID: 1,
|
||||||
|
PlanDescription: "hn",
|
||||||
|
}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 1,
|
||||||
|
"plan_description": "testing"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
expected := `{
|
||||||
|
"updated_plan": {
|
||||||
|
"plan_description": "testing",
|
||||||
|
"plan_id": 1,
|
||||||
|
"user_id": 3
|
||||||
|
},
|
||||||
|
"id": 1
|
||||||
|
}`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtraFieldPlanPutJSON(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
p := &models.Plan{
|
||||||
|
PlanID: 1,
|
||||||
|
PlanDescription: "hn",
|
||||||
|
}
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 1,
|
||||||
|
"plan_description": "testing",
|
||||||
|
"sabotage": "omg"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyBodyPlanPut(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data := []byte(``)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwoBodyPlanPut(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}, {
|
||||||
|
"plan_id": 6
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadPlanIDPut(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}, {
|
||||||
|
"plan_id": 6
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/text", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusNotFound, status)
|
||||||
|
expected := `Not Found`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorUpdatePlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
p := &models.Plan{PlanID: 6}
|
||||||
|
data, _ := json.Marshal(p)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorOnRetrieveUpdatePlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorOnGetModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
p := &models.Plan{PlanID: 6}
|
||||||
|
data, _ := json.Marshal(p)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorWriterUpdatePlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
p := &models.Plan{PlanID: 6}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data, _ := json.Marshal(p)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
188
routes/plans.go
Normal file
188
routes/plans.go
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewPlanRouter returns the http.Handler for the passed in model to route plan methods.
|
||||||
|
func NewPlanRouter(m *models.Model) http.Handler {
|
||||||
|
router := chi.NewRouter()
|
||||||
|
router.Get("/", getAllPlansFunc(m))
|
||||||
|
router.Post("/", postPlanFunc(m))
|
||||||
|
router.Get("/{planid}", getPlanByIDFunc(m))
|
||||||
|
router.Put("/{planid}", putPlanFunc(m))
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllPlansFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
userID, err := tokens.GetUserID(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
plans, err := m.Plans(userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(plans); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPlanByIDFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
userID, err := tokens.GetUserID(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := strconv.Atoi(chi.URLParam(r, "planid"))
|
||||||
|
if err != nil {
|
||||||
|
notFoundHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// todo get real user id
|
||||||
|
plan, err := m.Plan(id, userID)
|
||||||
|
if err != nil {
|
||||||
|
if models.IsNotFoundError(err) {
|
||||||
|
notFoundHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(plan); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type createPlanResponse struct {
|
||||||
|
CreatedPlan *models.Plan `json:"created_plan"`
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func postPlanFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
userID, err := tokens.GetUserID(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, 1024)
|
||||||
|
dec := json.NewDecoder(r.Body)
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
var p models.Plan
|
||||||
|
err = dec.Decode(&p)
|
||||||
|
if err != nil {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = dec.Decode(&struct{}{})
|
||||||
|
if err != io.EOF {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the fields we allow to be set to the plan to be created.
|
||||||
|
plan := &models.Plan{PlanDescription: p.PlanDescription, UserID: p.UserID}
|
||||||
|
id, err := m.AddPlan(plan, userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
plan, err = m.Plan(id, userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &createPlanResponse{
|
||||||
|
CreatedPlan: plan,
|
||||||
|
ID: int64(id),
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type updatePlanResponse struct {
|
||||||
|
UpdatedPlan *models.Plan `json:"updated_plan"`
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func putPlanFunc(m *models.Model) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
userID, err := tokens.GetUserID(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
unauthorizedHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := strconv.Atoi(chi.URLParam(r, "planid"))
|
||||||
|
if err != nil {
|
||||||
|
notFoundHandler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, 1024)
|
||||||
|
dec := json.NewDecoder(r.Body)
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
var p models.Plan
|
||||||
|
err = dec.Decode(&p)
|
||||||
|
if err != nil {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = dec.Decode(&struct{}{})
|
||||||
|
if err != io.EOF {
|
||||||
|
badRequestError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
plan := &models.Plan{
|
||||||
|
PlanDescription: p.PlanDescription,
|
||||||
|
PlanID: int64(id),
|
||||||
|
}
|
||||||
|
err = m.SavePlan(plan, userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
plan, err = m.Plan(id, userID)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response := &updatePlanResponse{
|
||||||
|
UpdatedPlan: plan,
|
||||||
|
ID: int64(id),
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
220
routes/plans_test.go
Normal file
220
routes/plans_test.go
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sampleContext = tokens.GetContextForUserValues(3, "testing")
|
||||||
|
|
||||||
|
func TestEmptyPlans(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
expected := `[]`
|
||||||
|
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOnePlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription, UserID: 3}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `[
|
||||||
|
{
|
||||||
|
"plan_id": 1,
|
||||||
|
"plan_description": "2021-01-01",
|
||||||
|
"user_id": 3
|
||||||
|
}
|
||||||
|
]`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorPlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyPlanErrorWriter(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOnePlanByID(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription, UserID: 3}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/1", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
|
||||||
|
expected := `{
|
||||||
|
"plan_id": 1,
|
||||||
|
"plan_description": "2021-01-01",
|
||||||
|
"user_id": 3
|
||||||
|
}`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorPlanByID(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/5", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyPlanErrorWriterByID(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
p := &models.Plan{PlanID: 1, PlanDescription: planDescription}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/1", nil)
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNotFoundPlanByIDText(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/wo", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusNotFound, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
func TestNotFoundPlanByIDEmpty(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getEmptyModel()
|
||||||
|
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/1", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusNotFound, status)
|
||||||
|
|
||||||
|
}
|
||||||
119
routes/plans_unauthorized_test.go
Normal file
119
routes/plans_unauthorized_test.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEmptyPlanEmptyContext(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOnePlanEmptyContext(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription, UserID: 3}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOnePlanByIDEmptyContext(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription, UserID: 3}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/1", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPureJSONEmptyContext(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_description": "2021-01-01T00:00:00Z",
|
||||||
|
"plan_id": 1,
|
||||||
|
"user_id": 3
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPutPlanEmptyContext(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: "sth"}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data, _ := json.Marshal(p)
|
||||||
|
req, _ := http.NewRequestWithContext(context.Background(), "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
|
||||||
|
}
|
||||||
215
routes/post_action_test.go
Normal file
215
routes/post_action_test.go
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPureJSONPostAction(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
compOn, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
a := &models.Action{
|
||||||
|
PlanID: 5,
|
||||||
|
CompletedOn: &compOn,
|
||||||
|
EstimatedChunks: 3,
|
||||||
|
CompletedChunks: 2,
|
||||||
|
ActionDescription: "here's an action",
|
||||||
|
}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddAction(a, 3)
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"action_description": "here's an action",
|
||||||
|
"estimated_chunks": 3,
|
||||||
|
"completed_chunks": 2,
|
||||||
|
"completed_on": "2021-01-01T00:00:00Z",
|
||||||
|
"plan_id": 5
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusCreated, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `{
|
||||||
|
"created_action": {
|
||||||
|
"action_description": "here's an action",
|
||||||
|
"estimated_chunks": 3,
|
||||||
|
"completed_chunks": 2,
|
||||||
|
"completed_on": "2021-01-01T00:00:00Z",
|
||||||
|
"plan_id": 5,
|
||||||
|
"action_id": 2,
|
||||||
|
"user_id": 3
|
||||||
|
},
|
||||||
|
"id": 2
|
||||||
|
}`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtraFieldActionPostJSON(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"completed_on": "2021-01-01T00:00:00Z",
|
||||||
|
"plan_id": 5,
|
||||||
|
"sabotage": "omg"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
func TestEmptyBodyActionPost(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data := []byte(``)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwoBodyActionPost(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}, {
|
||||||
|
"plan_id": 6
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorCreateAction(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
a := &models.Action{PlanID: 6}
|
||||||
|
data, _ := json.Marshal(a)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorOnRetrieveCreateAction(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorOnGetModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
a := &models.Action{PlanID: 6}
|
||||||
|
data, _ := json.Marshal(a)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorWriterCreateAction(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
a := &models.Action{PlanID: 6}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddAction(a, 3)
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data, _ := json.Marshal(a)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
243
routes/post_plan_test.go
Normal file
243
routes/post_plan_test.go
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreatePlanRoute(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
userID := 3
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription, UserID: int64(userID)}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, userID)
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data, _ := json.Marshal(p)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusCreated, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `{
|
||||||
|
"created_plan": {
|
||||||
|
"plan_id": 2,
|
||||||
|
"plan_description": "2021-01-01",
|
||||||
|
"user_id": 3
|
||||||
|
},
|
||||||
|
"id": 2
|
||||||
|
}`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPureJSON(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
userID := 3
|
||||||
|
p := &models.Plan{PlanID: 1, PlanDescription: planDescription, UserID: int64(userID)}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, userID)
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_description": "2021-01-01",
|
||||||
|
"plan_id": 1,
|
||||||
|
"user_id": 3
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusCreated, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `{
|
||||||
|
"created_plan": {
|
||||||
|
"plan_id": 2,
|
||||||
|
"user_id": 3,
|
||||||
|
"plan_description": "2021-01-01"
|
||||||
|
},
|
||||||
|
"id": 2
|
||||||
|
}`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtraFieldJSON(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
userID := 3
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription, UserID: int64(userID)}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, userID)
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_description": "2021-01-01",
|
||||||
|
"plan_id": 5,
|
||||||
|
"plan_sabotage": "omg"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
func TestEmptyBody(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
userID := 3
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, userID)
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data := []byte(``)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwoBody(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_description": "2021-01-01",
|
||||||
|
"plan_id": 5
|
||||||
|
}, {
|
||||||
|
"plan_description": "2021-01-01",
|
||||||
|
"plan_id": 6
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorCreatePlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription}
|
||||||
|
data, _ := json.Marshal(p)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorOnRetrieveCreatePlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorOnGetModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription}
|
||||||
|
data, _ := json.Marshal(p)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorWriterCreatePlan(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
|
||||||
|
p := &models.Plan{PlanID: 6, PlanDescription: planDescription}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddPlan(p, 3)
|
||||||
|
router := routes.NewPlanRouter(m)
|
||||||
|
data, _ := json.Marshal(p)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
231
routes/put_action_test.go
Normal file
231
routes/put_action_test.go
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPureJSONPutAction(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
compOn, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
a := &models.Action{
|
||||||
|
PlanID: 5,
|
||||||
|
CompletedOn: &compOn,
|
||||||
|
EstimatedChunks: 1,
|
||||||
|
CompletedChunks: 1,
|
||||||
|
ActionDescription: "hn",
|
||||||
|
}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddAction(a, 3)
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"action_description": "here's an action",
|
||||||
|
"estimated_chunks": 3,
|
||||||
|
"completed_chunks": 2,
|
||||||
|
"completed_on": "2021-01-01T00:00:00Z",
|
||||||
|
"plan_id": 5
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `{
|
||||||
|
"updated_action": {
|
||||||
|
"action_description": "here's an action",
|
||||||
|
"estimated_chunks": 3,
|
||||||
|
"completed_chunks": 2,
|
||||||
|
"completed_on": "2021-01-01T00:00:00Z",
|
||||||
|
"plan_id": 5,
|
||||||
|
"action_id": 1,
|
||||||
|
"user_id": 3
|
||||||
|
},
|
||||||
|
"id": 1
|
||||||
|
}`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtraFieldActionPutJSON(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"completed_on": "2021-01-01T00:00:00Z",
|
||||||
|
"plan_id": 5,
|
||||||
|
"sabotage": "omg"
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
func TestEmptyBodyActionPut(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data := []byte(``)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTwoBodyActionPut(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}, {
|
||||||
|
"plan_id": 6
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusBadRequest, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Bad Request`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadActionIDPut(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data := []byte(`{
|
||||||
|
"plan_id": 5
|
||||||
|
}, {
|
||||||
|
"plan_id": 6
|
||||||
|
}`)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/text", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusNotFound, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Not Found`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorUpdateAction(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
a := &models.Action{PlanID: 6}
|
||||||
|
data, _ := json.Marshal(a)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorOnRetrieveUpdateAction(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := getErrorOnGetModel("Model always errors")
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
a := &models.Action{PlanID: 6}
|
||||||
|
data, _ := json.Marshal(a)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||||
|
expected := `Internal Server Error`
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorWriterUpdateAction(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
a := &models.Action{PlanID: 6}
|
||||||
|
m := getEmptyModel()
|
||||||
|
m.AddAction(a, 3)
|
||||||
|
|
||||||
|
router := routes.NewActionRouter(m)
|
||||||
|
data, _ := json.Marshal(a)
|
||||||
|
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/1", bytes.NewBuffer(data))
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
|
||||||
|
}
|
||||||
22
routes/route_model_test.go
Normal file
22
routes/route_model_test.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getEmptyModel() *models.Model {
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
m := models.New(str)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func getErrorModel(message string) *models.Model {
|
||||||
|
str := store.GetErrorStore(message, true)
|
||||||
|
return models.New(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getErrorOnGetModel(errorMsg string) *models.Model {
|
||||||
|
str := store.GetErrorStore(errorMsg, false)
|
||||||
|
return models.New(str)
|
||||||
|
}
|
||||||
36
routes/routes.go
Normal file
36
routes/routes.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewRouter returns a router powered by the provided model.
|
||||||
|
func NewRouter(m *models.Model, tok tokens.Toker) http.Handler {
|
||||||
|
router := chi.NewRouter()
|
||||||
|
router.MethodNotAllowed(methodNotAllowedHandler)
|
||||||
|
router.NotFound(notFoundHandler)
|
||||||
|
router.Group(func(r chi.Router) {
|
||||||
|
r.Use(tok.Authenticator)
|
||||||
|
r.Mount("/actions", NewActionRouter(m))
|
||||||
|
r.Mount("/plans", NewPlanRouter(m))
|
||||||
|
r.Mount("/me", NewCurrentUserRouter(m))
|
||||||
|
r.Mount("/currentPlan", NewCurrentPlanRouter(m))
|
||||||
|
})
|
||||||
|
router.Mount("/auth", NewAuthRouter(m, tok))
|
||||||
|
router.Mount("/health", newHealthRouter(m))
|
||||||
|
router.Get("/ping", ping)
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
|
func ping(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// A very simple health check.
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if err := json.NewEncoder(w).Encode(map[string]string{"ping": "pong"}); err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
87
routes/routes_test.go
Normal file
87
routes/routes_test.go
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package routes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/routes"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPingHandler(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewRouter(m, tokens.New("whatever"))
|
||||||
|
req, _ := http.NewRequest("GET", "/ping", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
expected := `{"ping": "pong"}`
|
||||||
|
assert.JSONEq(expected, rr.Body.String())
|
||||||
|
contentType := rr.Header().Get("Content-Type")
|
||||||
|
assert.Equal("application/json", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPingPostHandler(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewRouter(m, tokens.New("whatever"))
|
||||||
|
req, _ := http.NewRequest("POST", "/ping", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusMethodNotAllowed, status)
|
||||||
|
expected := http.StatusText(http.StatusMethodNotAllowed)
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNotFoundHandler(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewRouter(m, tokens.New("whatever"))
|
||||||
|
req, _ := http.NewRequest("POST", "/null", nil)
|
||||||
|
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusNotFound, status)
|
||||||
|
expected := http.StatusText(http.StatusNotFound)
|
||||||
|
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPingError(t *testing.T) {
|
||||||
|
// set up
|
||||||
|
assert := assert.New(t)
|
||||||
|
m := getEmptyModel()
|
||||||
|
router := routes.NewRouter(m, tokens.New("whatever"))
|
||||||
|
req, _ := http.NewRequest("GET", "/ping", nil)
|
||||||
|
|
||||||
|
rr := NewBadWriter()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// check results
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusInternalServerError, status)
|
||||||
|
}
|
||||||
104
store/errorStore.go
Normal file
104
store/errorStore.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *errorStore) SelectActions(userID int) ([]*models.Action, error) {
|
||||||
|
return nil, e.error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) SelectActionByID(id int, userID int) (*models.Action, error) {
|
||||||
|
return nil, e.error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) InsertAction(action *models.Action, userID int) (int, error) {
|
||||||
|
if e.errorOnInsert {
|
||||||
|
return 0, e.error
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) UpdateAction(action *models.Action, userID int) error {
|
||||||
|
if e.errorOnInsert {
|
||||||
|
return e.error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) SelectPlans(userID int) ([]*models.Plan, error) {
|
||||||
|
return nil, e.error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) SelectPlanByID(id int, userID int) (*models.Plan, error) {
|
||||||
|
return nil, e.error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) InsertPlan(plan *models.Plan, userID int) (int, error) {
|
||||||
|
if e.errorOnInsert {
|
||||||
|
return 0, e.error
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) UpdatePlan(plan *models.Plan, userID int) error {
|
||||||
|
if e.errorOnInsert {
|
||||||
|
return e.error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) SelectActionsByPlanID(plan *models.Plan, userID int) ([]*models.Action, error) {
|
||||||
|
return nil, e.error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) SelectUserByUsername(name string) (*models.User, error) {
|
||||||
|
return nil, e.error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) InsertUser(user *models.User) (int, error) {
|
||||||
|
if e.errorOnInsert {
|
||||||
|
return 0, e.error
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) SelectCurrentPlan(userID int) (*models.CurrentPlan, error) {
|
||||||
|
return nil, e.error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) InsertCurrentPlan(currentPlan *models.CurrentPlan, userID int) error {
|
||||||
|
if e.errorOnInsert {
|
||||||
|
return e.error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) UpdateCurrentPlan(currentPlan *models.CurrentPlan, userID int) error {
|
||||||
|
if e.errorOnInsert {
|
||||||
|
return e.error
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *errorStore) ConnectionLive() error {
|
||||||
|
return e.error
|
||||||
|
}
|
||||||
|
|
||||||
|
type errorStore struct {
|
||||||
|
error error
|
||||||
|
errorOnInsert bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetErrorStore returns a models.Store that always errors. This is useful for testing purposes.
|
||||||
|
func GetErrorStore(errorMsg string, errorOnInsert bool) models.Store {
|
||||||
|
e := &errorStore{error: fmt.Errorf(errorMsg), errorOnInsert: errorOnInsert}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetErrorStoreForError returns a models.Store that always errors with the provided error.
|
||||||
|
func GetErrorStoreForError(err error, errorOnInsert bool) models.Store {
|
||||||
|
e := &errorStore{error: err, errorOnInsert: errorOnInsert}
|
||||||
|
return e
|
||||||
|
}
|
||||||
112
store/errorStore_test.go
Normal file
112
store/errorStore_test.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package store_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/store"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestErrorActionMethods(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
userID := 2
|
||||||
|
str := store.GetErrorStore("error message sample", true)
|
||||||
|
str2 := store.GetErrorStore("error message sample", false)
|
||||||
|
str3 := store.GetErrorStoreForError(fmt.Errorf("test error"), false)
|
||||||
|
|
||||||
|
_, err := str.InsertAction(&models.Action{}, userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
_, err = str2.InsertAction(&models.Action{}, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
_, err = str3.InsertAction(&models.Action{}, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
_, err = str.SelectActionByID(8, userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
_, err = str2.SelectActionByID(8, userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
_, err = str.SelectActions(userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
_, err = str.SelectActionsByPlanID(&models.Plan{}, userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
replacementAction := &models.Action{}
|
||||||
|
err = str.UpdateAction(replacementAction, userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
err = str2.UpdateAction(replacementAction, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorPlanMethods(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
str := store.GetErrorStore("sntahoeu", true)
|
||||||
|
str2 := store.GetErrorStore("sntahoeu", false)
|
||||||
|
|
||||||
|
_, err := str.SelectPlans(3)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
_, err = str.InsertPlan(&models.Plan{}, 3)
|
||||||
|
assert.NotNil(err)
|
||||||
|
_, err = str2.InsertPlan(&models.Plan{}, 3)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
replacementPlan := &models.Plan{}
|
||||||
|
err = str.UpdatePlan(replacementPlan, 3)
|
||||||
|
assert.NotNil(err)
|
||||||
|
err = str2.UpdatePlan(replacementPlan, 3)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
_, err = str.SelectPlanByID(5, 3)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorLive(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
str := store.GetErrorStore("error", true)
|
||||||
|
|
||||||
|
err := str.ConnectionLive()
|
||||||
|
assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorUserMethods(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
str := store.GetErrorStore("error", true)
|
||||||
|
str2 := store.GetErrorStore("error", false)
|
||||||
|
|
||||||
|
u := &models.User{}
|
||||||
|
|
||||||
|
_, err := str.InsertUser(u)
|
||||||
|
assert.NotNil(err)
|
||||||
|
_, err = str2.InsertUser(u)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
_, err = str.SelectUserByUsername("snth")
|
||||||
|
assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorCurrentPlanMethods(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
str := store.GetErrorStore("error", true)
|
||||||
|
str2 := store.GetErrorStore("error", false)
|
||||||
|
|
||||||
|
cp := &models.CurrentPlan{}
|
||||||
|
|
||||||
|
_, err := str.SelectCurrentPlan(1)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
err = str.InsertCurrentPlan(cp, 1)
|
||||||
|
assert.NotNil(err)
|
||||||
|
err = str2.InsertCurrentPlan(cp, 1)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
replace := &models.CurrentPlan{}
|
||||||
|
err = str.UpdateCurrentPlan(replace, 1)
|
||||||
|
assert.NotNil(err)
|
||||||
|
err = str2.UpdateCurrentPlan(replace, 1)
|
||||||
|
assert.Nil(err)
|
||||||
|
}
|
||||||
164
store/inmemory.go
Normal file
164
store/inmemory.go
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type inMemoryStore struct {
|
||||||
|
actions []*models.Action
|
||||||
|
plans []*models.Plan
|
||||||
|
users []*models.User
|
||||||
|
currentPlans []*models.CurrentPlan
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInMemoryStore provides a purely in memory store, for testing purposes only, with no persistence.
|
||||||
|
func GetInMemoryStore() (models.Store, error) {
|
||||||
|
return &inMemoryStore{
|
||||||
|
actions: make([]*models.Action, 0),
|
||||||
|
plans: make([]*models.Plan, 0),
|
||||||
|
users: make([]*models.User, 0),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) SelectActions(userID int) ([]*models.Action, error) {
|
||||||
|
ret := make([]*models.Action, 0)
|
||||||
|
for _, action := range store.actions {
|
||||||
|
if int(action.UserID) == userID {
|
||||||
|
ret = append(ret, action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) SelectActionsByPlanID(plan *models.Plan, userID int) ([]*models.Action, error) {
|
||||||
|
ret := make([]*models.Action, 0)
|
||||||
|
for _, action := range store.actions {
|
||||||
|
if (int(plan.PlanID) == int(action.PlanID)) && (int(action.UserID) == userID) {
|
||||||
|
ret = append(ret, action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) SelectActionByID(id int, userID int) (*models.Action, error) {
|
||||||
|
for _, action := range store.actions {
|
||||||
|
if id == int(action.ActionID) && (int(action.UserID) == userID) {
|
||||||
|
return action, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, sql.ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) InsertAction(action *models.Action, userID int) (int, error) {
|
||||||
|
id := len(store.actions) + 1
|
||||||
|
action.ActionID = int64(id)
|
||||||
|
action.UserID = int64(userID)
|
||||||
|
store.actions = append(store.actions, action)
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) UpdateAction(action *models.Action, userID int) error {
|
||||||
|
currentAction, err := store.SelectActionByID(int(action.ActionID), userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
currentAction.ActionDescription = action.ActionDescription
|
||||||
|
currentAction.EstimatedChunks = action.EstimatedChunks
|
||||||
|
currentAction.CompletedChunks = action.CompletedChunks
|
||||||
|
currentAction.PlanID = action.PlanID
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) SelectPlans(userID int) ([]*models.Plan, error) {
|
||||||
|
ret := make([]*models.Plan, 0)
|
||||||
|
for _, plan := range store.plans {
|
||||||
|
if int(plan.UserID) == userID {
|
||||||
|
ret = append(ret, plan)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) SelectPlanByID(id int, userID int) (*models.Plan, error) {
|
||||||
|
for _, plan := range store.plans {
|
||||||
|
if id == int(plan.PlanID) && (userID == int(plan.UserID)) {
|
||||||
|
return plan, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, sql.ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) InsertPlan(plan *models.Plan, userID int) (int, error) {
|
||||||
|
id := len(store.plans) + 1
|
||||||
|
plan.PlanID = int64(id)
|
||||||
|
plan.UserID = int64(userID)
|
||||||
|
store.plans = append(store.plans, plan)
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) UpdatePlan(plan *models.Plan, userID int) error {
|
||||||
|
currPlan, err := store.SelectPlanByID(int(plan.PlanID), userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
currPlan.PlanDescription = plan.PlanDescription
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) SelectCurrentPlan(userID int) (*models.CurrentPlan, error) {
|
||||||
|
for _, currentPlan := range store.currentPlans {
|
||||||
|
if userID == int(currentPlan.UserID) {
|
||||||
|
return currentPlan, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, sql.ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) InsertCurrentPlan(currentPlan *models.CurrentPlan, userID int) error {
|
||||||
|
_, err := store.SelectCurrentPlan(userID)
|
||||||
|
if err == nil {
|
||||||
|
return fmt.Errorf("Can't insert primary plan")
|
||||||
|
}
|
||||||
|
// actually impossible, but at this point it must be a not found error.
|
||||||
|
// if err != sql.ErrNoRows {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
|
||||||
|
store.currentPlans = append(store.currentPlans, &models.CurrentPlan{PlanID: int64(currentPlan.PlanID), UserID: int64(userID)})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) UpdateCurrentPlan(currentPlan *models.CurrentPlan, userID int) error {
|
||||||
|
current, err := store.SelectCurrentPlan(userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
current.PlanID = currentPlan.PlanID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) ConnectionLive() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *inMemoryStore) SelectUserByUsername(username string) (*models.User, error) {
|
||||||
|
for _, user := range store.users {
|
||||||
|
if username == user.Username {
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, sql.ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
// inMemoryStore.InsertUser will not enforce unique usernames, which is ok.
|
||||||
|
func (store *inMemoryStore) InsertUser(user *models.User) (int, error) {
|
||||||
|
id := len(store.users) + 1
|
||||||
|
user.UserID = int64(id)
|
||||||
|
store.users = append(store.users, user)
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
151
store/inmemory_test.go
Normal file
151
store/inmemory_test.go
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
package store_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/store"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInMemoryActionMethods(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
|
||||||
|
sampleplanid := 8
|
||||||
|
userID := 10
|
||||||
|
|
||||||
|
act := &models.Action{}
|
||||||
|
a2 := &models.Action{PlanID: sampleplanid}
|
||||||
|
|
||||||
|
id, _ := str.InsertAction(act, userID)
|
||||||
|
assert.EqualValues(1, id)
|
||||||
|
|
||||||
|
receivedAction, err := str.SelectActionByID(id, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(act, receivedAction)
|
||||||
|
_, err = str.SelectActionByID(id, userID+1)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
allactions, err := str.SelectActions(userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(1, len(allactions))
|
||||||
|
|
||||||
|
str.InsertAction(a2, userID)
|
||||||
|
allactions, err = str.SelectActions(userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(2, len(allactions))
|
||||||
|
|
||||||
|
planactions, err := str.SelectActionsByPlanID(&models.Plan{PlanID: int64(sampleplanid)}, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(1, len(planactions))
|
||||||
|
assert.Equal(a2, planactions[0])
|
||||||
|
|
||||||
|
_, err = str.SelectActionByID(151, userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
sampleDescription := "snth"
|
||||||
|
replacementAction := &models.Action{ActionID: 1, ActionDescription: sampleDescription}
|
||||||
|
err = str.UpdateAction(replacementAction, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(sampleDescription, act.ActionDescription)
|
||||||
|
|
||||||
|
replacementAction = &models.Action{ActionID: 1235122, ActionDescription: sampleDescription}
|
||||||
|
err = str.UpdateAction(replacementAction, userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInMemoryPlanMethods(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
userID := 1
|
||||||
|
p := &models.Plan{}
|
||||||
|
plans, err := str.SelectPlans(userID)
|
||||||
|
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(0, len(plans))
|
||||||
|
|
||||||
|
id, err := str.InsertPlan(p, userID)
|
||||||
|
plans, err = str.SelectPlans(userID)
|
||||||
|
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(1, len(plans))
|
||||||
|
|
||||||
|
retrievedPlan, err := str.SelectPlanByID(id, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(retrievedPlan, p)
|
||||||
|
|
||||||
|
_, err = str.SelectPlanByID(135135, userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
sampleDescription := "snth"
|
||||||
|
replacePlan := &models.Plan{PlanID: 1, PlanDescription: sampleDescription}
|
||||||
|
err = str.UpdatePlan(replacePlan, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(sampleDescription, p.PlanDescription)
|
||||||
|
|
||||||
|
replacePlan = &models.Plan{PlanID: 1235122, PlanDescription: sampleDescription}
|
||||||
|
err = str.UpdatePlan(replacePlan, userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLive(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
|
||||||
|
err := str.ConnectionLive()
|
||||||
|
assert.Nil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInMemoryUserMethods(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
|
||||||
|
uname := "hiimauser"
|
||||||
|
|
||||||
|
u := &models.User{Username: uname}
|
||||||
|
|
||||||
|
id, err := str.InsertUser(u)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
retrievedUser, err := str.SelectUserByUsername(uname)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(id, retrievedUser.UserID)
|
||||||
|
|
||||||
|
_, err = str.SelectUserByUsername("bad username")
|
||||||
|
assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInMemoryCurrentPlanMethods(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
str, _ := store.GetInMemoryStore()
|
||||||
|
|
||||||
|
userID := 10
|
||||||
|
|
||||||
|
cp1 := &models.CurrentPlan{PlanID: 1}
|
||||||
|
cp2 := &models.CurrentPlan{PlanID: 2}
|
||||||
|
|
||||||
|
err := str.UpdateCurrentPlan(cp1, userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
err = str.InsertCurrentPlan(cp1, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
receivedCp, err := str.SelectCurrentPlan(userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(1, receivedCp.PlanID)
|
||||||
|
|
||||||
|
_, err = str.SelectCurrentPlan(userID + 1)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
str.InsertCurrentPlan(cp2, userID)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
err = str.UpdateCurrentPlan(cp2, userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
receivedCp, err = str.SelectCurrentPlan(userID)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(2, receivedCp.PlanID)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
DROP TABLE IF EXISTS actions;
|
DROP TABLE IF EXISTS actions;
|
||||||
|
DROP TABLE IF EXISTS user_current_plan;
|
||||||
DROP TABLE IF EXISTS plans;
|
DROP TABLE IF EXISTS plans;
|
||||||
|
DROP TABLE IF EXISTS users;
|
||||||
|
|
||||||
DROP FUNCTION IF EXISTS trigger_set_timestamp;
|
DROP FUNCTION IF EXISTS trigger_set_timestamp;
|
||||||
|
|||||||
@@ -1,13 +1,31 @@
|
|||||||
CREATE TABLE IF NOT EXISTS plans(
|
CREATE TABLE IF NOT EXISTS users(
|
||||||
plan_id serial PRIMARY KEY,
|
user_id serial PRIMARY KEY,
|
||||||
plan_date DATE NOT NULL,
|
username VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
display_name VARCHAR (100) NOT NULL,
|
||||||
|
password bytea,
|
||||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS plans(
|
||||||
|
plan_id serial PRIMARY KEY,
|
||||||
|
plan_description VARCHAR (500) NOT NULL,
|
||||||
|
user_id int REFERENCES users(user_id),
|
||||||
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
UNIQUE (user_id, plan_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS user_current_plan(
|
||||||
|
user_id int PRIMARY KEY,
|
||||||
|
plan_id int,
|
||||||
|
FOREIGN KEY (user_id, plan_id) REFERENCES plans(user_id, plan_id)
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS actions(
|
CREATE TABLE IF NOT EXISTS actions(
|
||||||
action_id serial PRIMARY KEY,
|
action_id serial PRIMARY KEY,
|
||||||
action_description VARCHAR (500) NOT NULL,
|
action_description VARCHAR (500) NOT NULL,
|
||||||
|
user_id int REFERENCES users(user_id),
|
||||||
estimated_chunks SMALLINT,
|
estimated_chunks SMALLINT,
|
||||||
completed_chunks SMALLINT,
|
completed_chunks SMALLINT,
|
||||||
completed_on TIMESTAMP WITH TIME ZONE,
|
completed_on TIMESTAMP WITH TIME ZONE,
|
||||||
@@ -16,7 +34,6 @@ CREATE TABLE IF NOT EXISTS actions(
|
|||||||
plan_id int REFERENCES plans(plan_id)
|
plan_id int REFERENCES plans(plan_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION trigger_set_timestamp()
|
CREATE OR REPLACE FUNCTION trigger_set_timestamp()
|
||||||
RETURNS TRIGGER AS $set_updated$
|
RETURNS TRIGGER AS $set_updated$
|
||||||
BEGIN
|
BEGIN
|
||||||
@@ -34,3 +51,8 @@ CREATE TRIGGER set_updated
|
|||||||
BEFORE UPDATE ON plans
|
BEFORE UPDATE ON plans
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
EXECUTE PROCEDURE trigger_set_timestamp();
|
EXECUTE PROCEDURE trigger_set_timestamp();
|
||||||
|
|
||||||
|
CREATE TRIGGER set_updated
|
||||||
|
BEFORE UPDATE ON users
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE trigger_set_timestamp();
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
package store
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
|
|
||||||
"gitea.deepak.science/deepak/gogmagog/models"
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
"gitea.deepak.science/deepak/gogmagog/util"
|
"gitea.deepak.science/deepak/gogmagog/util"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type postgresStore struct {
|
type postgresStore struct {
|
||||||
@@ -18,38 +17,232 @@ func GetPostgresStore(db *sqlx.DB) (models.Store, error) {
|
|||||||
return &postgresStore{db: db}, nil
|
return &postgresStore{db: db}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *postgresStore) SelectActions() ([]*models.Action, error) {
|
func (store *postgresStore) SelectActions(userID int) ([]*models.Action, error) {
|
||||||
|
queryString := store.db.Rebind("SELECT action_id, action_description, user_id, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions WHERE user_id = ?")
|
||||||
actions := make([]*models.Action, 0)
|
actions := make([]*models.Action, 0)
|
||||||
err := store.db.Select(&actions, "SELECT action_id, action_description, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions")
|
err := store.db.Select(&actions, queryString, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return actions, nil
|
return actions, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *postgresStore) SelectActionByID(id int) (*models.Action, error) {
|
func (store *postgresStore) SelectActionsByPlanID(plan *models.Plan, userID int) ([]*models.Action, error) {
|
||||||
|
queryString := store.db.Rebind("SELECT action_id, action_description, user_id, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions WHERE plan_id = ? AND user_id = ?")
|
||||||
|
actions := make([]*models.Action, 0)
|
||||||
|
err := store.db.Select(&actions, queryString, plan.PlanID, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return actions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *postgresStore) SelectActionByID(id int, userID int) (*models.Action, error) {
|
||||||
|
queryString := store.db.Rebind("SELECT action_id, action_description, user_id, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions WHERE action_id = ? AND user_id = ?")
|
||||||
action := models.Action{}
|
action := models.Action{}
|
||||||
err := store.db.Get(&action, store.db.Rebind("SELECT action_id, action_description, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions WHERE action_id = ?"), id)
|
err := store.db.Get(&action, queryString, id, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &action, nil
|
return &action, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *postgresStore) SelectPlans() ([]*models.Plan, error) {
|
func (store *postgresStore) InsertAction(action *models.Action, userID int) (int, error) {
|
||||||
|
queryString := store.db.Rebind(
|
||||||
|
`INSERT INTO actions (action_description,
|
||||||
|
user_id,
|
||||||
|
estimated_chunks,
|
||||||
|
completed_chunks,
|
||||||
|
completed_on,
|
||||||
|
plan_id) VALUES (?, ?, ?, ?, ?, ?) RETURNING action_id`,
|
||||||
|
)
|
||||||
|
tx := store.db.MustBegin()
|
||||||
|
var id int
|
||||||
|
err := tx.Get(
|
||||||
|
&id,
|
||||||
|
queryString,
|
||||||
|
action.ActionDescription,
|
||||||
|
userID,
|
||||||
|
action.EstimatedChunks,
|
||||||
|
action.CompletedChunks,
|
||||||
|
action.CompletedOn,
|
||||||
|
action.PlanID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *postgresStore) UpdateAction(action *models.Action, userID int) error {
|
||||||
|
query := `UPDATE actions SET
|
||||||
|
action_description = :action_description,
|
||||||
|
estimated_chunks = :estimated_chunks,
|
||||||
|
completed_chunks = :completed_chunks,
|
||||||
|
completed_on = :completed_on,
|
||||||
|
plan_id = :plan_id
|
||||||
|
WHERE action_id = :action_id
|
||||||
|
AND user_id = :user_id`
|
||||||
|
tx := store.db.MustBegin()
|
||||||
|
actionToUse := &models.Action{
|
||||||
|
ActionDescription: action.ActionDescription,
|
||||||
|
EstimatedChunks: action.EstimatedChunks,
|
||||||
|
CompletedChunks: action.CompletedChunks,
|
||||||
|
CompletedOn: action.CompletedOn,
|
||||||
|
PlanID: action.PlanID,
|
||||||
|
ActionID: action.ActionID,
|
||||||
|
UserID: int64(userID),
|
||||||
|
}
|
||||||
|
_, err := store.db.NamedExec(query, actionToUse)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *postgresStore) SelectPlans(userID int) ([]*models.Plan, error) {
|
||||||
|
queryString := store.db.Rebind("SELECT plan_id, plan_description, user_id FROM plans WHERE user_id = ?")
|
||||||
plans := make([]*models.Plan, 0)
|
plans := make([]*models.Plan, 0)
|
||||||
err := store.db.Select(&plans, "SELECT plan_id, plan_date FROM plans")
|
err := store.db.Select(&plans, queryString, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return plans, nil
|
return plans, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *postgresStore) SelectPlanByID(id int) (*models.Plan, error) {
|
func (store *postgresStore) SelectPlanByID(id int, userID int) (*models.Plan, error) {
|
||||||
plan := models.Plan{}
|
plan := models.Plan{}
|
||||||
err := store.db.Get(&plan, store.db.Rebind("SELECT plan_id, plan_date FROM plans WHERE plan_id = ?"), id)
|
err := store.db.Get(&plan, store.db.Rebind("SELECT plan_id, plan_description, user_id FROM plans WHERE plan_id = ? AND user_id = ?"), id, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &plan, nil
|
return &plan, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (store *postgresStore) InsertPlan(plan *models.Plan, userID int) (int, error) {
|
||||||
|
queryString := store.db.Rebind("INSERT INTO plans (plan_description, user_id) VALUES (?, ?) RETURNING plan_id")
|
||||||
|
tx := store.db.MustBegin()
|
||||||
|
var id int
|
||||||
|
err := tx.Get(&id, queryString, plan.PlanDescription, userID)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *postgresStore) UpdatePlan(plan *models.Plan, userID int) error {
|
||||||
|
query := `UPDATE plans SET
|
||||||
|
plan_description = :plan_description
|
||||||
|
WHERE plan_id = :plan_id
|
||||||
|
AND user_id = :user_id`
|
||||||
|
tx := store.db.MustBegin()
|
||||||
|
planToUse := &models.Plan{
|
||||||
|
PlanDescription: plan.PlanDescription,
|
||||||
|
PlanID: plan.PlanID,
|
||||||
|
UserID: int64(userID),
|
||||||
|
}
|
||||||
|
_, err := store.db.NamedExec(query, planToUse)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *postgresStore) ConnectionLive() error {
|
||||||
|
return store.db.Ping()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *postgresStore) SelectUserByUsername(username string) (*models.User, error) {
|
||||||
|
user := models.User{}
|
||||||
|
err := store.db.Get(&user, store.db.Rebind("SELECT user_id, username, display_name, password FROM users WHERE username = ?"), username)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *postgresStore) InsertUser(user *models.User) (int, error) {
|
||||||
|
queryString := store.db.Rebind("INSERT INTO users (username, display_name, password) VALUES (?, ?, ?) RETURNING user_id")
|
||||||
|
tx := store.db.MustBegin()
|
||||||
|
var id int
|
||||||
|
err := tx.Get(&id, queryString, user.Username, user.DisplayName, user.Password)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *postgresStore) SelectCurrentPlan(userID int) (*models.CurrentPlan, error) {
|
||||||
|
pp := models.CurrentPlan{}
|
||||||
|
queryString := store.db.Rebind(`SELECT user_id, plan_id FROM user_current_plan WHERE user_id = ?`)
|
||||||
|
err := store.db.Get(&pp, queryString, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &pp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *postgresStore) InsertCurrentPlan(currentPlan *models.CurrentPlan, userID int) error {
|
||||||
|
queryString := store.db.Rebind("INSERT INTO user_current_plan (user_id, plan_id) VALUES (?, ?) RETURNING user_id")
|
||||||
|
tx := store.db.MustBegin()
|
||||||
|
var id int
|
||||||
|
err := tx.Get(&id, queryString, userID, currentPlan.PlanID)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *postgresStore) UpdateCurrentPlan(currentPlan *models.CurrentPlan, userID int) error {
|
||||||
|
query := `UPDATE user_current_plan SET
|
||||||
|
plan_id = :plan_id
|
||||||
|
WHERE user_id = :user_id`
|
||||||
|
tx := store.db.MustBegin()
|
||||||
|
ppToUse := &models.CurrentPlan{
|
||||||
|
PlanID: currentPlan.PlanID,
|
||||||
|
UserID: int64(userID),
|
||||||
|
}
|
||||||
|
_, err := store.db.NamedExec(query, ppToUse)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = tx.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
217
store/postgres_current_plan_test.go
Normal file
217
store/postgres_current_plan_test.go
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
package store_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSelectCurrentPlan(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
planIDToUse := 1
|
||||||
|
userIDToUse := 2
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{"plan_id", "user_id"}).AddRow(planIDToUse, userIDToUse)
|
||||||
|
mock.ExpectQuery(`^SELECT user_id, plan_id FROM user_current_plan WHERE user_id = \$1`).
|
||||||
|
WithArgs(userIDToUse).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
|
||||||
|
currPlan, err := str.SelectCurrentPlan(userIDToUse)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(planIDToUse, currPlan.PlanID)
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectCurrentPlanErr(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
userIDToUse := 2
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
mock.ExpectQuery(`^SELECT user_id, plan_id FROM user_current_plan WHERE user_id = \$1`).
|
||||||
|
WithArgs(userIDToUse).
|
||||||
|
WillReturnError(fmt.Errorf("example error"))
|
||||||
|
|
||||||
|
currPlan, err := str.SelectCurrentPlan(userIDToUse)
|
||||||
|
assert.NotNil(err)
|
||||||
|
assert.Nil(currPlan)
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertCurrentPlan(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
planID := 1
|
||||||
|
userID := 2
|
||||||
|
badUserID := 7
|
||||||
|
|
||||||
|
cp := &models.CurrentPlan{PlanID: int64(planID), UserID: int64(badUserID)}
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{"userID"}).AddRow(userID)
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectQuery(`^INSERT INTO user_current_plan \(user_id, plan_id\) VALUES \(\$1, \$2\) RETURNING user_id$`).
|
||||||
|
WithArgs(userID, planID).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
mock.ExpectCommit()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
err := str.InsertCurrentPlan(cp, userID)
|
||||||
|
// check results
|
||||||
|
assert.Nil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertCurrentPlanErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
userID := 2
|
||||||
|
badUserID := 7
|
||||||
|
planID := 1
|
||||||
|
cp := &models.CurrentPlan{PlanID: int64(planID), UserID: int64(badUserID)}
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectQuery(`^INSERT INTO user_current_plan \(user_id, plan_id\) VALUES \(\$1, \$2\) RETURNING user_id$`).
|
||||||
|
WithArgs(userID, planID).
|
||||||
|
WillReturnError(fmt.Errorf("example error"))
|
||||||
|
mock.ExpectRollback()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
err := str.InsertCurrentPlan(cp, userID)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertCurrentPlanCommitErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
planID := 1
|
||||||
|
userID := 2
|
||||||
|
cp := &models.CurrentPlan{PlanID: int64(planID), UserID: int64(userID)}
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{"user_id"}).AddRow(userID)
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectQuery(`^INSERT INTO user_current_plan \(user_id, plan_id\) VALUES \(\$1, \$2\) RETURNING user_id$`).
|
||||||
|
WithArgs(userID, planID).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
mock.ExpectCommit().WillReturnError(fmt.Errorf("another error example"))
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
err := str.InsertCurrentPlan(cp, userID)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateCurrentPlan(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
userIDToUse := 1
|
||||||
|
planIDToUse := 2
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectExec(`
|
||||||
|
UPDATE user_current_plan SET
|
||||||
|
plan_id = \$1
|
||||||
|
WHERE user_id = \$2`).
|
||||||
|
WithArgs(planIDToUse, userIDToUse).
|
||||||
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
mock.ExpectCommit()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
err := str.UpdateCurrentPlan(&models.CurrentPlan{PlanID: int64(planIDToUse)}, userIDToUse)
|
||||||
|
// check results
|
||||||
|
assert.Nil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateCurrentPlanErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
userIDToUse := 1
|
||||||
|
planIDToUse := 2
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectExec(`
|
||||||
|
UPDATE user_current_plan SET
|
||||||
|
plan_id = \$1
|
||||||
|
WHERE user_id = \$2`).
|
||||||
|
WithArgs(planIDToUse, userIDToUse).
|
||||||
|
WillReturnError(fmt.Errorf("example error"))
|
||||||
|
mock.ExpectRollback()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
err := str.UpdateCurrentPlan(&models.CurrentPlan{PlanID: int64(planIDToUse)}, userIDToUse)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateCurrentPlanCommitErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
userIDToUse := 1
|
||||||
|
planIDToUse := 2
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectExec(`
|
||||||
|
UPDATE user_current_plan SET
|
||||||
|
plan_id = \$1
|
||||||
|
WHERE user_id = \$2`).
|
||||||
|
WithArgs(planIDToUse, userIDToUse).
|
||||||
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
mock.ExpectCommit().WillReturnError(fmt.Errorf("another error example"))
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
err := str.UpdateCurrentPlan(&models.CurrentPlan{PlanID: int64(planIDToUse)}, userIDToUse)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
264
store/postgres_plan_test.go
Normal file
264
store/postgres_plan_test.go
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
package store_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSelectPlans(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
planDesc := "testing"
|
||||||
|
idToUse := 1
|
||||||
|
userIDToUse := 2
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{"plan_id", "plan_description", "user_id"}).AddRow(idToUse, planDesc, userIDToUse)
|
||||||
|
mock.ExpectQuery(`^SELECT plan_id, plan_description, user_id FROM plans WHERE user_id = \$1`).
|
||||||
|
WithArgs(userIDToUse).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
|
||||||
|
plans, err := str.SelectPlans(userIDToUse)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(1, len(plans))
|
||||||
|
plan := plans[0]
|
||||||
|
assert.EqualValues(idToUse, plan.PlanID)
|
||||||
|
assert.Equal(planDesc, plan.PlanDescription)
|
||||||
|
assert.EqualValues(userIDToUse, plan.UserID)
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectPlanByID(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
planDesc := "tsaoeu"
|
||||||
|
idToUse := 1
|
||||||
|
userIDToUse := 2
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{"plan_id", "plan_description", "user_id"}).AddRow(idToUse, planDesc, userIDToUse)
|
||||||
|
mock.ExpectQuery(`^SELECT plan_id, plan_description, user_id FROM plans WHERE plan_id = \$1 AND user_id = \$2$`).
|
||||||
|
WithArgs(idToUse, userIDToUse).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
|
||||||
|
plan, err := str.SelectPlanByID(idToUse, userIDToUse)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(idToUse, plan.PlanID)
|
||||||
|
assert.Equal(planDesc, plan.PlanDescription)
|
||||||
|
assert.EqualValues(userIDToUse, plan.UserID)
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertPlan(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
userID := 2
|
||||||
|
badUserID := 7
|
||||||
|
|
||||||
|
plan := &models.Plan{PlanDescription: planDescription, UserID: int64(badUserID)}
|
||||||
|
|
||||||
|
idToUse := 8
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{"plan_id"}).AddRow(8)
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectQuery(`^INSERT INTO plans \(plan_description, user_id\) VALUES \(\$1, \$2\) RETURNING plan_id$`).
|
||||||
|
WithArgs(planDescription, userID).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
mock.ExpectCommit()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
insertedId, err := str.InsertPlan(plan, userID)
|
||||||
|
// check results
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(idToUse, insertedId)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertPlanErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
userID := 2
|
||||||
|
badUserID := 7
|
||||||
|
plan := &models.Plan{PlanDescription: planDescription, UserID: int64(badUserID)}
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectQuery(`^INSERT INTO plans \(plan_description, user_id\) VALUES \(\$1, \$2\) RETURNING plan_id$`).
|
||||||
|
WithArgs(planDescription, userID).
|
||||||
|
WillReturnError(fmt.Errorf("example error"))
|
||||||
|
mock.ExpectRollback()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
_, err := str.InsertPlan(plan, userID)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertPlanCommitErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
userID := 2
|
||||||
|
plan := &models.Plan{PlanDescription: planDescription, UserID: int64(userID)}
|
||||||
|
idToUse := 8
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{"plan_id"}).AddRow(idToUse)
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectQuery(`^INSERT INTO plans \(plan_description, user_id\) VALUES \(\$1, \$2\) RETURNING plan_id$`).
|
||||||
|
WithArgs(planDescription, userID).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
mock.ExpectCommit().WillReturnError(fmt.Errorf("another error example"))
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
_, err := str.InsertPlan(plan, userID)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrPlanByID(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
idToUse := 1
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
mock.ExpectQuery(`^SELECT plan_id, plan_description, user_id FROM plans WHERE plan_id = \$1 AND user_id = \$2$`).
|
||||||
|
WithArgs(idToUse, 8).
|
||||||
|
WillReturnError(fmt.Errorf("example error"))
|
||||||
|
|
||||||
|
plan, err := str.SelectPlanByID(idToUse, 8)
|
||||||
|
assert.NotNil(err)
|
||||||
|
assert.Nil(plan)
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrPlans(t *testing.T) {
|
||||||
|
// set up tests
|
||||||
|
assert := assert.New(t)
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
mock.ExpectQuery(`^SELECT plan_id, plan_description, user_id FROM plans WHERE user_id = \$1$`).
|
||||||
|
WithArgs(8).
|
||||||
|
WillReturnError(fmt.Errorf("example error"))
|
||||||
|
// function under test
|
||||||
|
plans, err := str.SelectPlans(8)
|
||||||
|
// test results
|
||||||
|
assert.Nil(plans)
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdatePlan(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
userID := 2
|
||||||
|
idToUse := 8
|
||||||
|
|
||||||
|
plan := &models.Plan{PlanDescription: planDescription, UserID: int64(userID), PlanID: int64(idToUse)}
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectExec(`^UPDATE plans SET plan_description = \$1 WHERE plan_id = \$2 AND user_id = \$3$`).
|
||||||
|
WithArgs(planDescription, idToUse, userID).
|
||||||
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
mock.ExpectCommit()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
err := str.UpdatePlan(plan, userID)
|
||||||
|
// check results
|
||||||
|
assert.Nil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func TestUpdatePlanErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
userID := 2
|
||||||
|
idToUse := 8
|
||||||
|
|
||||||
|
plan := &models.Plan{PlanDescription: planDescription, UserID: int64(userID), PlanID: int64(idToUse)}
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectExec(`^UPDATE plans SET plan_description = \$1 WHERE plan_id = \$2 AND user_id = \$3$`).
|
||||||
|
WithArgs(planDescription, idToUse, userID).
|
||||||
|
WillReturnError(fmt.Errorf("example error"))
|
||||||
|
mock.ExpectRollback()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
err := str.UpdatePlan(plan, userID)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdatePlanCommitErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
planDescription := "2021-01-01"
|
||||||
|
userID := 2
|
||||||
|
idToUse := 8
|
||||||
|
|
||||||
|
plan := &models.Plan{PlanDescription: planDescription, UserID: int64(userID), PlanID: int64(idToUse)}
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectExec(`^UPDATE plans SET plan_description = \$1 WHERE plan_id = \$2 AND user_id = \$3$`).
|
||||||
|
WithArgs(planDescription, idToUse, userID).
|
||||||
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
mock.ExpectCommit().WillReturnError(fmt.Errorf("another error example"))
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
err := str.UpdatePlan(plan, userID)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func getDbMock(t *testing.T) (models.Store, sqlmock.Sqlmock) {
|
func getDbMock(t *testing.T) (models.Store, sqlmock.Sqlmock) {
|
||||||
db, mock, err := sqlmock.New()
|
db, mock, err := sqlmock.New(sqlmock.MonitorPingsOption(true))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("got an error creating a stub db. How?: %s", err)
|
t.Fatalf("got an error creating a stub db. How?: %s", err)
|
||||||
}
|
}
|
||||||
@@ -24,68 +24,6 @@ func getDbMock(t *testing.T) (models.Store, sqlmock.Sqlmock) {
|
|||||||
return str, mock
|
return str, mock
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSelectPlans(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
currentTime := time.Now()
|
|
||||||
idToUse := 1
|
|
||||||
|
|
||||||
str, mock := getDbMock(t)
|
|
||||||
|
|
||||||
rows := sqlmock.NewRows([]string{"plan_id", "plan_date"}).AddRow(idToUse, currentTime)
|
|
||||||
mock.ExpectQuery("^SELECT plan_id, plan_date FROM plans$").WillReturnRows(rows)
|
|
||||||
|
|
||||||
plans, err := str.SelectPlans()
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(1, len(plans))
|
|
||||||
plan := plans[0]
|
|
||||||
assert.EqualValues(idToUse, plan.PlanID)
|
|
||||||
assert.Equal(currentTime, plan.PlanDate)
|
|
||||||
|
|
||||||
if err := mock.ExpectationsWereMet(); err != nil {
|
|
||||||
t.Errorf("unfulfilled expectations: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSelectPlanByID(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
currentTime := time.Now()
|
|
||||||
idToUse := 1
|
|
||||||
|
|
||||||
str, mock := getDbMock(t)
|
|
||||||
|
|
||||||
rows := sqlmock.NewRows([]string{"plan_id", "plan_date"}).AddRow(idToUse, currentTime)
|
|
||||||
mock.ExpectQuery("^SELECT plan_id, plan_date FROM plans WHERE plan_id = \\$1$").WithArgs(idToUse).WillReturnRows(rows)
|
|
||||||
|
|
||||||
plan, err := str.SelectPlanByID(idToUse)
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.EqualValues(idToUse, plan.PlanID)
|
|
||||||
assert.Equal(currentTime, plan.PlanDate)
|
|
||||||
|
|
||||||
if err := mock.ExpectationsWereMet(); err != nil {
|
|
||||||
t.Errorf("unfulfilled expectations: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrPlanByID(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
idToUse := 1
|
|
||||||
|
|
||||||
str, mock := getDbMock(t)
|
|
||||||
|
|
||||||
mock.ExpectQuery("^SELECT plan_id, plan_date FROM plans WHERE plan_id = \\$1$").WithArgs(idToUse).WillReturnError(fmt.Errorf("example error"))
|
|
||||||
|
|
||||||
plan, err := str.SelectPlanByID(idToUse)
|
|
||||||
assert.NotNil(err)
|
|
||||||
assert.Nil(plan)
|
|
||||||
|
|
||||||
if err := mock.ExpectationsWereMet(); err != nil {
|
|
||||||
t.Errorf("unfulfilled expectations: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSelectActions(t *testing.T) {
|
func TestSelectActions(t *testing.T) {
|
||||||
// set up test
|
// set up test
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
@@ -96,6 +34,7 @@ func TestSelectActions(t *testing.T) {
|
|||||||
idToUse := 1
|
idToUse := 1
|
||||||
estChunks := 5
|
estChunks := 5
|
||||||
compChunks := 7
|
compChunks := 7
|
||||||
|
userIDToUse := 13
|
||||||
desc := "Howdy, partner."
|
desc := "Howdy, partner."
|
||||||
|
|
||||||
str, mock := getDbMock(t)
|
str, mock := getDbMock(t)
|
||||||
@@ -108,13 +47,16 @@ func TestSelectActions(t *testing.T) {
|
|||||||
"completed_on",
|
"completed_on",
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
"plan_id"}).
|
"plan_id",
|
||||||
AddRow(idToUse, desc, estChunks, compChunks, completeTime, createTime, updateTime, idToUse).
|
"user_id"}).
|
||||||
AddRow(idToUse+1, desc, estChunks, compChunks, completeTime, createTime, updateTime, idToUse)
|
AddRow(idToUse, desc, estChunks, compChunks, completeTime, createTime, updateTime, idToUse, userIDToUse).
|
||||||
mock.ExpectQuery("^SELECT action_id, action_description, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions$").WillReturnRows(rows)
|
AddRow(idToUse+1, desc, estChunks, compChunks, completeTime, createTime, updateTime, idToUse, userIDToUse)
|
||||||
|
mock.ExpectQuery(`^SELECT action_id, action_description, user_id, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions WHERE user_id = \$1$`).
|
||||||
|
WithArgs(userIDToUse).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
|
||||||
// function under test
|
// function under test
|
||||||
actions, err := str.SelectActions()
|
actions, err := str.SelectActions(userIDToUse)
|
||||||
|
|
||||||
// test results
|
// test results
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
@@ -124,10 +66,89 @@ func TestSelectActions(t *testing.T) {
|
|||||||
assert.Equal(desc, action.ActionDescription)
|
assert.Equal(desc, action.ActionDescription)
|
||||||
assert.Equal(estChunks, action.EstimatedChunks)
|
assert.Equal(estChunks, action.EstimatedChunks)
|
||||||
assert.Equal(compChunks, action.CompletedChunks)
|
assert.Equal(compChunks, action.CompletedChunks)
|
||||||
assert.Equal(completeTime, action.CompletedOn)
|
assert.Equal(completeTime, *action.CompletedOn)
|
||||||
assert.Equal(createTime, action.CreatedAt)
|
assert.Equal(createTime, *action.CreatedAt)
|
||||||
assert.Equal(updateTime, action.UpdatedAt)
|
assert.Equal(updateTime, *action.UpdatedAt)
|
||||||
assert.Equal(idToUse, action.PlanID)
|
assert.Equal(idToUse, action.PlanID)
|
||||||
|
assert.EqualValues(userIDToUse, action.UserID)
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectActionsByPlanID(t *testing.T) {
|
||||||
|
// set up test
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
createTime, _ := time.Parse("2006-01-02", "2020-12-31")
|
||||||
|
updateTime, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
completeTime, _ := time.Parse("2006-01-02", "2021-01-05")
|
||||||
|
idToUse := 1
|
||||||
|
estChunks := 5
|
||||||
|
compChunks := 7
|
||||||
|
userIDToUse := 13
|
||||||
|
desc := "Howdy, partner."
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{
|
||||||
|
"action_id",
|
||||||
|
"action_description",
|
||||||
|
"user_id",
|
||||||
|
"estimated_chunks",
|
||||||
|
"completed_chunks",
|
||||||
|
"completed_on",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"plan_id"}).
|
||||||
|
AddRow(idToUse, desc, userIDToUse, estChunks, compChunks, completeTime, createTime, updateTime, idToUse).
|
||||||
|
AddRow(idToUse+1, desc, userIDToUse, estChunks, compChunks, completeTime, createTime, updateTime, idToUse)
|
||||||
|
mock.ExpectQuery(`^SELECT action_id, action_description, user_id, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions WHERE plan_id = \$1 AND user_id = \$2$`).
|
||||||
|
WithArgs(idToUse, userIDToUse).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
actions, err := str.SelectActionsByPlanID(&models.Plan{PlanID: int64(idToUse)}, userIDToUse)
|
||||||
|
|
||||||
|
// test results
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(2, len(actions))
|
||||||
|
action := actions[0]
|
||||||
|
assert.EqualValues(idToUse, action.ActionID)
|
||||||
|
assert.Equal(desc, action.ActionDescription)
|
||||||
|
assert.Equal(estChunks, action.EstimatedChunks)
|
||||||
|
assert.Equal(compChunks, action.CompletedChunks)
|
||||||
|
assert.Equal(completeTime, *action.CompletedOn)
|
||||||
|
assert.Equal(createTime, *action.CreatedAt)
|
||||||
|
assert.Equal(updateTime, *action.UpdatedAt)
|
||||||
|
assert.Equal(idToUse, action.PlanID)
|
||||||
|
assert.EqualValues(userIDToUse, action.UserID)
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectActionsByPlanIDErr(t *testing.T) {
|
||||||
|
// set up test
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
idToUse := 1
|
||||||
|
userIDToUse := 13
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
mock.ExpectQuery(`^SELECT action_id, action_description, user_id, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions WHERE plan_id = \$1 AND user_id = \$2$`).
|
||||||
|
WithArgs(idToUse, userIDToUse).
|
||||||
|
WillReturnError(fmt.Errorf("example error"))
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
actions, err := str.SelectActionsByPlanID(&models.Plan{PlanID: int64(idToUse)}, userIDToUse)
|
||||||
|
|
||||||
|
// test results
|
||||||
|
assert.NotNil(err)
|
||||||
|
assert.Nil(actions)
|
||||||
|
|
||||||
if err := mock.ExpectationsWereMet(); err != nil {
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
t.Errorf("unfulfilled expectations: %s", err)
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
@@ -144,6 +165,7 @@ func TestSelectActionById(t *testing.T) {
|
|||||||
idToUse := 1
|
idToUse := 1
|
||||||
estChunks := 5
|
estChunks := 5
|
||||||
compChunks := 7
|
compChunks := 7
|
||||||
|
userIDToUse := 13
|
||||||
desc := "Howdy, partner."
|
desc := "Howdy, partner."
|
||||||
|
|
||||||
str, mock := getDbMock(t)
|
str, mock := getDbMock(t)
|
||||||
@@ -151,20 +173,21 @@ func TestSelectActionById(t *testing.T) {
|
|||||||
rows := sqlmock.NewRows([]string{
|
rows := sqlmock.NewRows([]string{
|
||||||
"action_id",
|
"action_id",
|
||||||
"action_description",
|
"action_description",
|
||||||
|
"user_id",
|
||||||
"estimated_chunks",
|
"estimated_chunks",
|
||||||
"completed_chunks",
|
"completed_chunks",
|
||||||
"completed_on",
|
"completed_on",
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
"plan_id"}).
|
"plan_id"}).
|
||||||
AddRow(idToUse, desc, estChunks, compChunks, completeTime, createTime, updateTime, idToUse)
|
AddRow(idToUse, desc, userIDToUse, estChunks, compChunks, completeTime, createTime, updateTime, idToUse)
|
||||||
|
|
||||||
mock.ExpectQuery("^SELECT action_id, action_description, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions WHERE action_id = \\$1").
|
mock.ExpectQuery(`^SELECT action_id, action_description, user_id, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions WHERE action_id = \$1 AND user_id = \$2`).
|
||||||
WithArgs(1).
|
WithArgs(1, userIDToUse).
|
||||||
WillReturnRows(rows)
|
WillReturnRows(rows)
|
||||||
|
|
||||||
// function under test
|
// function under test
|
||||||
action, err := str.SelectActionByID(1)
|
action, err := str.SelectActionByID(1, userIDToUse)
|
||||||
|
|
||||||
// test results
|
// test results
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
@@ -173,40 +196,25 @@ func TestSelectActionById(t *testing.T) {
|
|||||||
assert.Equal(desc, action.ActionDescription)
|
assert.Equal(desc, action.ActionDescription)
|
||||||
assert.Equal(estChunks, action.EstimatedChunks)
|
assert.Equal(estChunks, action.EstimatedChunks)
|
||||||
assert.Equal(compChunks, action.CompletedChunks)
|
assert.Equal(compChunks, action.CompletedChunks)
|
||||||
assert.Equal(completeTime, action.CompletedOn)
|
assert.Equal(completeTime, *action.CompletedOn)
|
||||||
assert.Equal(createTime, action.CreatedAt)
|
assert.Equal(createTime, *action.CreatedAt)
|
||||||
assert.Equal(updateTime, action.UpdatedAt)
|
assert.Equal(updateTime, *action.UpdatedAt)
|
||||||
assert.Equal(idToUse, action.PlanID)
|
assert.Equal(idToUse, action.PlanID)
|
||||||
|
assert.EqualValues(userIDToUse, action.UserID)
|
||||||
|
|
||||||
if err := mock.ExpectationsWereMet(); err != nil {
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
t.Errorf("unfulfilled expectations: %s", err)
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestErrPlans(t *testing.T) {
|
|
||||||
// set up tests
|
|
||||||
assert := assert.New(t)
|
|
||||||
str, mock := getDbMock(t)
|
|
||||||
|
|
||||||
mock.ExpectQuery("^SELECT plan_id, plan_date FROM plans$").WillReturnError(fmt.Errorf("example error"))
|
|
||||||
// function under test
|
|
||||||
plans, err := str.SelectPlans()
|
|
||||||
// test results
|
|
||||||
assert.Nil(plans)
|
|
||||||
assert.NotNil(err)
|
|
||||||
if err := mock.ExpectationsWereMet(); err != nil {
|
|
||||||
t.Errorf("unfulfilled expectations: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrActions(t *testing.T) {
|
func TestErrActions(t *testing.T) {
|
||||||
// set up tests
|
// set up tests
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
str, mock := getDbMock(t)
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
mock.ExpectQuery("^SELECT action_id, action_description, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions$").WillReturnError(fmt.Errorf("example error"))
|
mock.ExpectQuery(`^SELECT action_id, action_description, user_id, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions WHERE user_id = \$1$`).WithArgs(1).WillReturnError(fmt.Errorf("example error"))
|
||||||
// function under test
|
// function under test
|
||||||
actions, err := str.SelectActions()
|
actions, err := str.SelectActions(1)
|
||||||
// test results
|
// test results
|
||||||
assert.Nil(actions)
|
assert.Nil(actions)
|
||||||
assert.NotNil(err)
|
assert.NotNil(err)
|
||||||
@@ -220,9 +228,11 @@ func TestErrActionByID(t *testing.T) {
|
|||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
str, mock := getDbMock(t)
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
mock.ExpectQuery("^SELECT action_id, action_description, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions WHERE action_id = \\$1").WillReturnError(fmt.Errorf("example error"))
|
userIDToUse := 3
|
||||||
|
|
||||||
|
mock.ExpectQuery(`^SELECT action_id, action_description, user_id, estimated_chunks, completed_chunks, completed_on, created_at, updated_at, plan_id FROM actions WHERE action_id = \$1 AND user_id = \$2`).WithArgs(1, userIDToUse).WillReturnError(fmt.Errorf("example error"))
|
||||||
// function under test
|
// function under test
|
||||||
action, err := str.SelectActionByID(1)
|
action, err := str.SelectActionByID(1, userIDToUse)
|
||||||
// test results
|
// test results
|
||||||
assert.Nil(action)
|
assert.Nil(action)
|
||||||
assert.NotNil(err)
|
assert.NotNil(err)
|
||||||
@@ -230,3 +240,219 @@ func TestErrActionByID(t *testing.T) {
|
|||||||
t.Errorf("unfulfilled expectations: %s", err)
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConnectionLive(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
mock.ExpectPing()
|
||||||
|
|
||||||
|
// perform func under tests
|
||||||
|
err := str.ConnectionLive()
|
||||||
|
// results
|
||||||
|
assert.Nil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertAction(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
userIDToUse := 7
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
completedOn, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
action := &models.Action{
|
||||||
|
CompletedOn: &completedOn,
|
||||||
|
EstimatedChunks: 3,
|
||||||
|
CompletedChunks: 6,
|
||||||
|
PlanID: 5,
|
||||||
|
ActionDescription: "testing",
|
||||||
|
}
|
||||||
|
|
||||||
|
idToUse := 8
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{"action_id"}).AddRow(8)
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectQuery(`^INSERT INTO actions \(action_description, user_id, estimated_chunks, completed_chunks, completed_on, plan_id\) VALUES \(\$1, \$2, \$3, \$4, \$5, \$6\) RETURNING action_id$`).
|
||||||
|
WithArgs("testing", userIDToUse, 3, 6, completedOn, 5).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
mock.ExpectCommit()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
insertedId, err := str.InsertAction(action, userIDToUse)
|
||||||
|
// check results
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(idToUse, insertedId)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertActionErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
completedOn, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
action := &models.Action{
|
||||||
|
CompletedOn: &completedOn,
|
||||||
|
EstimatedChunks: 3,
|
||||||
|
CompletedChunks: 6,
|
||||||
|
PlanID: 5,
|
||||||
|
ActionDescription: "testing",
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectQuery(`^INSERT INTO actions \(action_description, user_id, estimated_chunks, completed_chunks, completed_on, plan_id\) VALUES \(\$1, \$2, \$3, \$4, \$5, \$6\) RETURNING action_id$`).
|
||||||
|
WithArgs("testing", 7, 3, 6, completedOn, 5).
|
||||||
|
WillReturnError(fmt.Errorf("example error"))
|
||||||
|
mock.ExpectRollback()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
_, err := str.InsertAction(action, 7)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertActionCommitErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
completedOn, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
action := &models.Action{
|
||||||
|
CompletedOn: &completedOn,
|
||||||
|
EstimatedChunks: 3,
|
||||||
|
CompletedChunks: 6,
|
||||||
|
PlanID: 5,
|
||||||
|
ActionDescription: "testing",
|
||||||
|
}
|
||||||
|
idToUse := 8
|
||||||
|
userIDToUse := 11
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{"plan_id"}).AddRow(idToUse)
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectQuery(`^INSERT INTO actions \(action_description, user_id, estimated_chunks, completed_chunks, completed_on, plan_id\) VALUES \(\$1, \$2, \$3, \$4, \$5, \$6\) RETURNING action_id$`).
|
||||||
|
WithArgs("testing", userIDToUse, 3, 6, completedOn, 5).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
mock.ExpectCommit().WillReturnError(fmt.Errorf("another error example"))
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
_, err := str.InsertAction(action, userIDToUse)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateAction(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
completedOn, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
action := &models.Action{
|
||||||
|
CompletedOn: &completedOn,
|
||||||
|
EstimatedChunks: 3,
|
||||||
|
CompletedChunks: 6,
|
||||||
|
PlanID: 5,
|
||||||
|
ActionDescription: "testing",
|
||||||
|
ActionID: 2,
|
||||||
|
}
|
||||||
|
userIDToUse := 31
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectExec(`
|
||||||
|
UPDATE actions SET
|
||||||
|
action_description = \$1,
|
||||||
|
estimated_chunks = \$2,
|
||||||
|
completed_chunks = \$3,
|
||||||
|
completed_on = \$4,
|
||||||
|
plan_id = \$5
|
||||||
|
WHERE action_id = \$6
|
||||||
|
AND user_id = \$7`).
|
||||||
|
WithArgs("testing", 3, 6, completedOn, 5, 2, userIDToUse).
|
||||||
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
mock.ExpectCommit()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
err := str.UpdateAction(action, userIDToUse)
|
||||||
|
// check results
|
||||||
|
assert.Nil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateActionErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
completedOn, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
action := &models.Action{
|
||||||
|
CompletedOn: &completedOn,
|
||||||
|
EstimatedChunks: 3,
|
||||||
|
CompletedChunks: 6,
|
||||||
|
PlanID: 5,
|
||||||
|
ActionDescription: "testing",
|
||||||
|
ActionID: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectExec("UPDATE actions").
|
||||||
|
WithArgs("testing", 3, 6, completedOn, 5, 2, 31).
|
||||||
|
WillReturnError(fmt.Errorf("example error"))
|
||||||
|
mock.ExpectRollback()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
err := str.UpdateAction(action, 31)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateActionCommitErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
completedOn, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||||
|
action := &models.Action{
|
||||||
|
CompletedOn: &completedOn,
|
||||||
|
EstimatedChunks: 3,
|
||||||
|
CompletedChunks: 6,
|
||||||
|
PlanID: 5,
|
||||||
|
ActionDescription: "testing",
|
||||||
|
ActionID: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectExec("UPDATE actions").
|
||||||
|
WithArgs("testing", 3, 6, completedOn, 5, 2, 31).
|
||||||
|
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
mock.ExpectCommit().WillReturnError(fmt.Errorf("another error example"))
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
err := str.UpdateAction(action, 31)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
150
store/postgres_user_test.go
Normal file
150
store/postgres_user_test.go
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
package store_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSelectUserByUsername(t *testing.T) {
|
||||||
|
// set up test
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
id := 1
|
||||||
|
username := "test"
|
||||||
|
displayName := "Tom Est"
|
||||||
|
password := []byte("ABC€")
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{
|
||||||
|
"user_id",
|
||||||
|
"username",
|
||||||
|
"display_name",
|
||||||
|
"password",
|
||||||
|
}).
|
||||||
|
AddRow(id, username, displayName, password)
|
||||||
|
|
||||||
|
mock.ExpectQuery(`^SELECT user_id, username, display_name, password FROM users WHERE username = \$1`).
|
||||||
|
WithArgs(username).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
user, err := str.SelectUserByUsername(username)
|
||||||
|
|
||||||
|
// test results
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(id, user.UserID)
|
||||||
|
assert.Equal(username, user.Username)
|
||||||
|
assert.Equal(displayName, user.DisplayName)
|
||||||
|
assert.Equal(password, user.Password)
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrUserByID(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
username := "snth"
|
||||||
|
mock.ExpectQuery(`^SELECT user_id, username, display_name, password FROM users WHERE username = \$1`).WithArgs(username).WillReturnError(fmt.Errorf("example error"))
|
||||||
|
|
||||||
|
user, err := str.SelectUserByUsername(username)
|
||||||
|
assert.NotNil(err)
|
||||||
|
assert.Nil(user)
|
||||||
|
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertUser(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
username := "test"
|
||||||
|
displayName := "Tom Est"
|
||||||
|
password := []byte("ABC€")
|
||||||
|
usr := &models.User{Username: username, DisplayName: displayName, Password: password}
|
||||||
|
|
||||||
|
idToUse := 8
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{"user_id"}).AddRow(8)
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectQuery(`^INSERT INTO users \(username, display_name, password\) VALUES \(\$1, \$2, \$3\) RETURNING user_id$`).
|
||||||
|
WithArgs(username, displayName, password).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
mock.ExpectCommit()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
insertedId, err := str.InsertUser(usr)
|
||||||
|
// check results
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(idToUse, insertedId)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertUserErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
username := "test"
|
||||||
|
displayName := "Tom Est"
|
||||||
|
password := []byte("ABC€")
|
||||||
|
usr := &models.User{Username: username, DisplayName: displayName, Password: password}
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectQuery(`^INSERT INTO users \(username, display_name, password\) VALUES \(\$1, \$2, \$3\) RETURNING user_id$`).
|
||||||
|
WithArgs(username, displayName, password).
|
||||||
|
WillReturnError(fmt.Errorf("example error"))
|
||||||
|
mock.ExpectRollback()
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
_, err := str.InsertUser(usr)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInsertUserCommitErr(t *testing.T) {
|
||||||
|
// setup
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
str, mock := getDbMock(t)
|
||||||
|
username := "test"
|
||||||
|
displayName := "Tom Est"
|
||||||
|
password := []byte("ABC€")
|
||||||
|
usr := &models.User{Username: username, DisplayName: displayName, Password: password}
|
||||||
|
|
||||||
|
idToUse := 8
|
||||||
|
|
||||||
|
rows := sqlmock.NewRows([]string{"user_id"}).AddRow(idToUse)
|
||||||
|
|
||||||
|
mock.ExpectBegin()
|
||||||
|
mock.ExpectQuery(`^INSERT INTO users \(username, display_name, password\) VALUES \(\$1, \$2, \$3\) RETURNING user_id$`).
|
||||||
|
WithArgs(username, displayName, password).
|
||||||
|
WillReturnRows(rows)
|
||||||
|
mock.ExpectCommit().WillReturnError(fmt.Errorf("another error example"))
|
||||||
|
|
||||||
|
// function under test
|
||||||
|
_, err := str.InsertUser(usr)
|
||||||
|
// check results
|
||||||
|
assert.NotNil(err)
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Errorf("unfulfilled expectations: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -40,32 +40,38 @@ func createPostgresDB(dbConf *config.DBConfig) (*sqlx.DB, error) {
|
|||||||
|
|
||||||
tmp, err := sql.Open("pgx", connStr)
|
tmp, err := sql.Open("pgx", connStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Could not connect to database: \n", err)
|
log.Print("Could not connect to database: \n", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
db := sqlx.NewDb(tmp, "pgx")
|
db := sqlx.NewDb(tmp, "pgx")
|
||||||
if err := db.Ping(); err != nil {
|
if err := db.Ping(); err != nil {
|
||||||
log.Fatal("database ping failed\n", err)
|
log.Print("database ping failed\n", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
driver, err := postgres.WithInstance(db.DB, &postgres.Config{})
|
driver, err := postgres.WithInstance(db.DB, &postgres.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Could not create driver for db migration", err)
|
log.Print("Could not create driver for db migration", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m, err := migrate.NewWithDatabaseInstance("file://store/migrations", "postgres", driver)
|
m, err := migrate.NewWithDatabaseInstance("file://store/migrations", "postgres", driver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Could not perform migration", err)
|
log.Print("Could not perform migration", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if dbConf.DropOnStart {
|
||||||
|
log.Print("Going down")
|
||||||
|
m.Down()
|
||||||
|
}
|
||||||
if err := m.Up(); err != nil {
|
if err := m.Up(); err != nil {
|
||||||
if err == migrate.ErrNoChange {
|
if err == migrate.ErrNoChange {
|
||||||
log.Print("No migration needed.")
|
log.Print("No migration needed.")
|
||||||
} else {
|
} else {
|
||||||
log.Fatalf("An error occurred while syncing the database.. %v", err)
|
log.Printf("An error occurred while syncing the database.. %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
log.Print("Performed database migration")
|
||||||
}
|
}
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|||||||
53
tokens/deterministicToker.go
Normal file
53
tokens/deterministicToker.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package tokens
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type deterministicToker struct{}
|
||||||
|
|
||||||
|
// GetDeterministicToker returns a zero security toker for testing purposes.
|
||||||
|
// Do not use in production.
|
||||||
|
func GetDeterministicToker() Toker {
|
||||||
|
return &deterministicToker{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *deterministicToker) EncodeUser(user *models.UserNoPassword) string {
|
||||||
|
tok := &UserToken{ID: user.UserID, Username: user.Username}
|
||||||
|
ret, _ := json.Marshal(tok)
|
||||||
|
return string(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *deterministicToker) DecodeTokenString(tokenString string) (*UserToken, error) {
|
||||||
|
var tok UserToken
|
||||||
|
err := json.Unmarshal([]byte(tokenString), &tok)
|
||||||
|
return &tok, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *deterministicToker) Authenticator(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tokenString := TokenFromHeader(r)
|
||||||
|
if tokenString == "" {
|
||||||
|
log.Print("No valid token found")
|
||||||
|
unauthorized(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userToken, err := d.DecodeTokenString(tokenString)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error while verifying token: %s", err)
|
||||||
|
unauthorized(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Got user with ID: [%d]", userToken.ID)
|
||||||
|
ctx := context.WithValue(r.Context(), userIDCtxKey, userToken.ID)
|
||||||
|
ctx = context.WithValue(ctx, usernameCtxKey, userToken.Username)
|
||||||
|
// Authenticated
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
80
tokens/deterministic_toker_middleware_test.go
Normal file
80
tokens/deterministic_toker_middleware_test.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package tokens_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dtMiddlewareURL string = "/"
|
||||||
|
|
||||||
|
func dtRequestAuth(header string) *http.Request {
|
||||||
|
req, _ := http.NewRequest("GET", dtMiddlewareURL, nil)
|
||||||
|
req.Header.Add(authKey, header)
|
||||||
|
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyingHandlerdt(t *testing.T, username string, userID int) http.Handler {
|
||||||
|
assert := assert.New(t)
|
||||||
|
toker := tokens.GetDeterministicToker()
|
||||||
|
dummyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
receivedID, _ := tokens.GetUserID(ctx)
|
||||||
|
receivedUsername, _ := tokens.GetUsername(ctx)
|
||||||
|
assert.EqualValues(userID, receivedID)
|
||||||
|
assert.Equal(username, receivedUsername)
|
||||||
|
})
|
||||||
|
return toker.Authenticator(dummyHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMiddlewareNoTokendt(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, dtMiddlewareURL, nil)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
middlewareHandler := verifyingHandlerdt(t, "", 0)
|
||||||
|
middlewareHandler.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMiddlewareBadTokendt(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
req := mwRequestAuth("Bearer bad")
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
middlewareHandler := verifyingHandlerdt(t, "", 0)
|
||||||
|
middlewareHandler.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMiddlewareGoodTokendt(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
idToUse := 3
|
||||||
|
username := "username"
|
||||||
|
displayName := "display name"
|
||||||
|
user := &models.UserNoPassword{UserID: int64(idToUse), Username: username, DisplayName: displayName}
|
||||||
|
|
||||||
|
toker := tokens.GetDeterministicToker()
|
||||||
|
validToken := toker.EncodeUser(user)
|
||||||
|
log.Print(validToken)
|
||||||
|
req := mwRequestAuth("Bearer " + validToken)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
middlewareHandler := verifyingHandlerdt(t, username, idToUse)
|
||||||
|
middlewareHandler.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
}
|
||||||
88
tokens/middleware.go
Normal file
88
tokens/middleware.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package tokens
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type contextKey struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
var userIDCtxKey = &contextKey{"UserID"}
|
||||||
|
var usernameCtxKey = &contextKey{"Username"}
|
||||||
|
|
||||||
|
func unauthorized(w http.ResponseWriter, r *http.Request) {
|
||||||
|
code := http.StatusUnauthorized
|
||||||
|
http.Error(w, http.StatusText(code), code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokenFromHeader tries to retreive the token string from the
|
||||||
|
// "Authorization" reqeust header: "Authorization: BEARER T".
|
||||||
|
func TokenFromHeader(r *http.Request) string {
|
||||||
|
// Get token from authorization header.
|
||||||
|
bearer := r.Header.Get("Authorization")
|
||||||
|
if len(bearer) > 7 && strings.ToUpper(bearer[0:6]) == "BEARER" {
|
||||||
|
return bearer[7:]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tok *jwtToker) Authenticator(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tokenString := TokenFromHeader(r)
|
||||||
|
if tokenString == "" {
|
||||||
|
log.Print("No valid token found")
|
||||||
|
unauthorized(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userToken, err := tok.DecodeTokenString(tokenString)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error while verifying token: %s", err)
|
||||||
|
unauthorized(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Got user with ID: [%d]", userToken.ID)
|
||||||
|
ctx := context.WithValue(r.Context(), userIDCtxKey, userToken.ID)
|
||||||
|
ctx = context.WithValue(ctx, usernameCtxKey, userToken.Username)
|
||||||
|
// Authenticated
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserID is a convenience method that gets the user ID from the context.
|
||||||
|
// I hate the fact that we're passing user ID on the context, but it is more
|
||||||
|
// idiomatic Go than any type shenanigans.
|
||||||
|
func GetUserID(ctx context.Context) (int, error) {
|
||||||
|
userID, ok := ctx.Value(userIDCtxKey).(int64)
|
||||||
|
if !ok {
|
||||||
|
return -1, fmt.Errorf("Could not parse user ID [%s] from context", ctx.Value(userIDCtxKey))
|
||||||
|
|
||||||
|
}
|
||||||
|
return int(userID), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUserID sets the username field on a context, necessary because the key is an unexported custom type.
|
||||||
|
func SetUserID(ctx context.Context, id int) context.Context {
|
||||||
|
return context.WithValue(ctx, userIDCtxKey, int64(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsername does something similar to GetUserID.
|
||||||
|
func GetUsername(ctx context.Context) (string, error) {
|
||||||
|
username, ok := ctx.Value(usernameCtxKey).(string)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("Could not parse username [%s] from context", ctx.Value(usernameCtxKey))
|
||||||
|
}
|
||||||
|
return username, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContextForUserValues is a test helper method that creates a context with user ID set.
|
||||||
|
func GetContextForUserValues(userID int, username string) context.Context {
|
||||||
|
ctx := context.WithValue(context.Background(), userIDCtxKey, int64(userID))
|
||||||
|
return context.WithValue(ctx, usernameCtxKey, username)
|
||||||
|
}
|
||||||
49
tokens/middleware_context_test.go
Normal file
49
tokens/middleware_context_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package tokens_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGoodContext(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
idToUse := 3
|
||||||
|
username := "username"
|
||||||
|
|
||||||
|
ctx := tokens.GetContextForUserValues(idToUse, username)
|
||||||
|
|
||||||
|
receivedID, err := tokens.GetUserID(ctx)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(idToUse, receivedID)
|
||||||
|
|
||||||
|
receivedUsername, err := tokens.GetUsername(ctx)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(username, receivedUsername)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadContext(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
_, err := tokens.GetUserID(ctx)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
_, err = tokens.GetUsername(ctx)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetContext(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
idToUse := 3
|
||||||
|
ctx := tokens.SetUserID(context.Background(), 3)
|
||||||
|
receivedID, err := tokens.GetUserID(ctx)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.EqualValues(idToUse, receivedID)
|
||||||
|
}
|
||||||
78
tokens/middleware_http_test.go
Normal file
78
tokens/middleware_http_test.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package tokens_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var middlewareURL string = "/"
|
||||||
|
|
||||||
|
func mwRequestAuth(header string) *http.Request {
|
||||||
|
req, _ := http.NewRequest("GET", middlewareURL, nil)
|
||||||
|
req.Header.Add(authKey, header)
|
||||||
|
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyingHandler(t *testing.T, username string, userID int) http.Handler {
|
||||||
|
assert := assert.New(t)
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
dummyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
receivedID, _ := tokens.GetUserID(ctx)
|
||||||
|
receivedUsername, _ := tokens.GetUsername(ctx)
|
||||||
|
assert.EqualValues(userID, receivedID)
|
||||||
|
assert.Equal(username, receivedUsername)
|
||||||
|
})
|
||||||
|
return toker.Authenticator(dummyHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMiddlewareNoToken(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, middlewareURL, nil)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
middlewareHandler := verifyingHandler(t, "", 0)
|
||||||
|
middlewareHandler.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMiddlewareBadToken(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
req := mwRequestAuth("Bearer bad")
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
middlewareHandler := verifyingHandler(t, "", 0)
|
||||||
|
middlewareHandler.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusUnauthorized, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMiddlewareGoodToken(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
idToUse := 3
|
||||||
|
username := "username"
|
||||||
|
displayName := "display name"
|
||||||
|
user := &models.UserNoPassword{UserID: int64(idToUse), Username: username, DisplayName: displayName}
|
||||||
|
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
validToken := toker.EncodeUser(user)
|
||||||
|
req := mwRequestAuth("Bearer " + validToken)
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
|
||||||
|
middlewareHandler := verifyingHandler(t, username, idToUse)
|
||||||
|
middlewareHandler.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
status := rr.Code
|
||||||
|
assert.Equal(http.StatusOK, status)
|
||||||
|
}
|
||||||
56
tokens/middleware_test.go
Normal file
56
tokens/middleware_test.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package tokens_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
url = ""
|
||||||
|
authKey = "Authorization"
|
||||||
|
)
|
||||||
|
|
||||||
|
func requestWithAuth(header string) *http.Request {
|
||||||
|
req, _ := http.NewRequest("GET", url, nil)
|
||||||
|
req.Header.Add(authKey, header)
|
||||||
|
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHeaderParseBasic(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
header := "Bearer testing"
|
||||||
|
req := requestWithAuth(header)
|
||||||
|
|
||||||
|
assert.Equal("testing", tokens.TokenFromHeader(req))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHeaderParseNoSpace(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
header := "Bearerxtesting"
|
||||||
|
req := requestWithAuth(header)
|
||||||
|
|
||||||
|
assert.Equal("testing", tokens.TokenFromHeader(req))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHeaderParseUnicode(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
header := "Bearer 🌸"
|
||||||
|
req := requestWithAuth(header)
|
||||||
|
|
||||||
|
assert.Equal("🌸", tokens.TokenFromHeader(req))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHeaderParseMalformed(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
header := "testing"
|
||||||
|
req := requestWithAuth(header)
|
||||||
|
|
||||||
|
assert.Equal("", tokens.TokenFromHeader(req))
|
||||||
|
}
|
||||||
86
tokens/tokens.go
Normal file
86
tokens/tokens.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package tokens
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"github.com/go-chi/jwtauth"
|
||||||
|
"github.com/lestrrat-go/jwx/jwt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Toker represents a tokenizer, capable of encoding and verifying tokens.
|
||||||
|
type Toker interface {
|
||||||
|
EncodeUser(user *models.UserNoPassword) string
|
||||||
|
DecodeTokenString(tokenString string) (*UserToken, error)
|
||||||
|
Authenticator(http.Handler) http.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
type jwtToker struct {
|
||||||
|
tokenAuth *jwtauth.JWTAuth
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a default Toker for a given secret key.
|
||||||
|
func New(key string) Toker {
|
||||||
|
return &jwtToker{tokenAuth: jwtauth.New("HS256", []byte(key), nil)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tok *jwtToker) EncodeUser(user *models.UserNoPassword) string {
|
||||||
|
claims := map[string]interface{}{
|
||||||
|
"user_id": user.UserID,
|
||||||
|
"username": user.Username,
|
||||||
|
"display_name": user.DisplayName,
|
||||||
|
"iss": "gogmagog.deepak.science",
|
||||||
|
"aud": "gogmagog.deepak.science",
|
||||||
|
}
|
||||||
|
jwtauth.SetIssuedNow(claims)
|
||||||
|
jwtauth.SetExpiryIn(claims, 2*time.Hour)
|
||||||
|
_, tokenString, _ := tok.tokenAuth.Encode(claims)
|
||||||
|
return tokenString
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserToken represents a decoded jwt token.
|
||||||
|
type UserToken struct {
|
||||||
|
ID int64
|
||||||
|
Username string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tok *jwtToker) DecodeTokenString(tokenString string) (*UserToken, error) {
|
||||||
|
token, err := tok.tokenAuth.Decode(tokenString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error decoding token")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should never happen, remove soon.
|
||||||
|
// if token == nil {
|
||||||
|
// return nil, fmt.Errorf("Token was nil")
|
||||||
|
// }
|
||||||
|
|
||||||
|
err = jwt.Validate(
|
||||||
|
token,
|
||||||
|
jwt.WithIssuer("gogmagog.deepak.science"),
|
||||||
|
jwt.WithAudience("gogmagog.deepak.science"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userIDRaw, ok := token.Get("user_id")
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("error finding user_id claim")
|
||||||
|
}
|
||||||
|
userID, ok := userIDRaw.(float64)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Could not parse [%s] as userID", userIDRaw)
|
||||||
|
}
|
||||||
|
usernameRaw, ok := token.Get("username")
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("error finding username claim")
|
||||||
|
}
|
||||||
|
username, ok := usernameRaw.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Could not parse [%s] as username", usernameRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &UserToken{ID: int64(userID), Username: username}, nil
|
||||||
|
}
|
||||||
165
tokens/tokens_test.go
Normal file
165
tokens/tokens_test.go
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
package tokens_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/models"
|
||||||
|
"gitea.deepak.science/deepak/gogmagog/tokens"
|
||||||
|
"github.com/go-chi/jwtauth"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBasic(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
idToUse := int64(3)
|
||||||
|
usernameToUse := "test"
|
||||||
|
usr := &models.UserNoPassword{
|
||||||
|
UserID: idToUse,
|
||||||
|
Username: usernameToUse,
|
||||||
|
DisplayName: "Ted Est III",
|
||||||
|
}
|
||||||
|
token := toker.EncodeUser(usr)
|
||||||
|
|
||||||
|
userToken, err := toker.DecodeTokenString(token)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Equal(usernameToUse, userToken.Username)
|
||||||
|
assert.Equal(idToUse, userToken.ID)
|
||||||
|
_, err = tokens.New("bad secret").DecodeTokenString(token)
|
||||||
|
assert.NotNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTokenString(claims map[string]interface{}) string {
|
||||||
|
auth := jwtauth.New("HS256", []byte("secret"), nil)
|
||||||
|
|
||||||
|
jwtauth.SetIssuedNow(claims)
|
||||||
|
jwtauth.SetExpiryIn(claims, 2*time.Hour)
|
||||||
|
_, tokenString, _ := auth.Encode(claims)
|
||||||
|
|
||||||
|
return tokenString
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBadIssuer(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
|
||||||
|
idToUse := 3
|
||||||
|
username := "test"
|
||||||
|
gog := "gogmagog.deepak.science"
|
||||||
|
|
||||||
|
claims := map[string]interface{}{
|
||||||
|
"user_id": int64(idToUse),
|
||||||
|
"username": username,
|
||||||
|
"display_name": "display_name",
|
||||||
|
"iss": gog,
|
||||||
|
"aud": "bad",
|
||||||
|
}
|
||||||
|
|
||||||
|
token := getTokenString(claims)
|
||||||
|
_, err := toker.DecodeTokenString(token)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBadAudience(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
|
||||||
|
idToUse := 3
|
||||||
|
username := "test"
|
||||||
|
gog := "gogmagog.deepak.science"
|
||||||
|
|
||||||
|
claims := map[string]interface{}{
|
||||||
|
"user_id": int64(idToUse),
|
||||||
|
"username": username,
|
||||||
|
"display_name": "display_name",
|
||||||
|
"iss": "bad",
|
||||||
|
"aud": gog,
|
||||||
|
}
|
||||||
|
|
||||||
|
token := getTokenString(claims)
|
||||||
|
_, err := toker.DecodeTokenString(token)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeMissingUserID(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
|
||||||
|
username := "test"
|
||||||
|
gog := "gogmagog.deepak.science"
|
||||||
|
|
||||||
|
claims := map[string]interface{}{
|
||||||
|
"username": username,
|
||||||
|
"display_name": "display_name",
|
||||||
|
"iss": gog,
|
||||||
|
"aud": gog,
|
||||||
|
}
|
||||||
|
|
||||||
|
token := getTokenString(claims)
|
||||||
|
_, err := toker.DecodeTokenString(token)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBadUserID(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
|
||||||
|
username := "test"
|
||||||
|
gog := "gogmagog.deepak.science"
|
||||||
|
|
||||||
|
claims := map[string]interface{}{
|
||||||
|
"username": username,
|
||||||
|
"user_id": "id",
|
||||||
|
"display_name": "display_name",
|
||||||
|
"iss": gog,
|
||||||
|
"aud": gog,
|
||||||
|
}
|
||||||
|
|
||||||
|
token := getTokenString(claims)
|
||||||
|
_, err := toker.DecodeTokenString(token)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeMissingUsername(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
|
||||||
|
idToUse := 3
|
||||||
|
gog := "gogmagog.deepak.science"
|
||||||
|
|
||||||
|
claims := map[string]interface{}{
|
||||||
|
"user_id": int64(idToUse),
|
||||||
|
"display_name": "display_name",
|
||||||
|
"iss": gog,
|
||||||
|
"aud": gog,
|
||||||
|
}
|
||||||
|
|
||||||
|
token := getTokenString(claims)
|
||||||
|
_, err := toker.DecodeTokenString(token)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBadUsername(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
toker := tokens.New("secret")
|
||||||
|
|
||||||
|
gog := "gogmagog.deepak.science"
|
||||||
|
|
||||||
|
claims := map[string]interface{}{
|
||||||
|
"username": 5,
|
||||||
|
"user_id": 3,
|
||||||
|
"display_name": "display_name",
|
||||||
|
"iss": gog,
|
||||||
|
"aud": gog,
|
||||||
|
}
|
||||||
|
|
||||||
|
token := getTokenString(claims)
|
||||||
|
_, err := toker.DecodeTokenString(token)
|
||||||
|
assert.NotNil(err)
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user