forked from vikunja/vikunja
Add postgres support (#135)
Revert fixture fixes for postgres Use postgres connection string with spaces instead of url Fix label order Make postgres tests in ci less verbose Add sequence update script Skip resets in postgres Remove option to skip resets in postgres Make postgres tests in ci verboseq Update test fixtures database Fix file tests on postgres Add postgres options to sample config Make sure tests init test fixtures before running the actual tests Fix issues with IDs too big to fit in an int Fix duplicate auto incremented IDs Refactor / Fix team tests Refactor team member tests Fix team member create Fix label test Fix getting labels Fix test fixtures for postgresql Fix connection string params Disable ssl mode on postgres integration tests Disable ssl mode on postgres tests Use sprintf to create the connection string for postgresql fixup! Add postgres support Add postgres support Added generate as a make dependency for make build Clarify docs on building Co-authored-by: kolaente <k@knt.li> Co-authored-by: Jan Tojnar <jtojnar@gmail.com> Reviewed-on: vikunja/api#135
This commit is contained in:
parent
7e42724439
commit
ce5be947b4
52
.drone1.yml
52
.drone1.yml
@ -6,16 +6,26 @@ workspace:
|
||||
path: src/code.vikunja.io/api
|
||||
|
||||
services:
|
||||
- name: test-db-unit
|
||||
- name: test-mysql-unit
|
||||
image: mariadb:10
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: vikunjatest
|
||||
MYSQL_DATABASE: vikunjatest
|
||||
- name: test-db-integration
|
||||
- name: test-mysql-integration
|
||||
image: mariadb:10
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: vikunjatest
|
||||
MYSQL_DATABASE: vikunjatest
|
||||
- name: test-postgres-unit
|
||||
image: postgres:12
|
||||
environment:
|
||||
POSTGRES_PASSWORD: vikunjatest
|
||||
POSTGRES_DB: vikunjatest
|
||||
- name: test-postgres-integration
|
||||
image: postgres:12
|
||||
environment:
|
||||
POSTGRES_PASSWORD: vikunjatest
|
||||
POSTGRES_DB: vikunjatest
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
@ -78,7 +88,7 @@ steps:
|
||||
environment:
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_HOST: test-db-unit
|
||||
VIKUNJA_DATABASE_HOST: test-mysql-unit
|
||||
VIKUNJA_DATABASE_USER: root
|
||||
VIKUNJA_DATABASE_PASSWORD: vikunjatest
|
||||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
@ -88,6 +98,23 @@ steps:
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: test-postgres
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
VIKUNJA_DATABASE_TYPE: postgres
|
||||
VIKUNJA_DATABASE_HOST: test-postgres-unit
|
||||
VIKUNJA_DATABASE_USER: postgres
|
||||
VIKUNJA_DATABASE_PASSWORD: vikunjatest
|
||||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
VIKUNJA_DATABASE_SSLMODE: disable
|
||||
commands:
|
||||
- make test
|
||||
depends_on: [ build ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: integration-test
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
@ -115,7 +142,7 @@ steps:
|
||||
environment:
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
VIKUNJA_DATABASE_TYPE: mysql
|
||||
VIKUNJA_DATABASE_HOST: test-db-integration
|
||||
VIKUNJA_DATABASE_HOST: test-mysql-integration
|
||||
VIKUNJA_DATABASE_USER: root
|
||||
VIKUNJA_DATABASE_PASSWORD: vikunjatest
|
||||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
@ -125,6 +152,23 @@ steps:
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
- name: integration-test-postgres
|
||||
image: vikunja/golang-build:latest
|
||||
pull: true
|
||||
environment:
|
||||
VIKUNJA_TESTS_USE_CONFIG: 1
|
||||
VIKUNJA_DATABASE_TYPE: postgres
|
||||
VIKUNJA_DATABASE_HOST: test-postgres-integration
|
||||
VIKUNJA_DATABASE_USER: postgres
|
||||
VIKUNJA_DATABASE_PASSWORD: vikunjatest
|
||||
VIKUNJA_DATABASE_DATABASE: vikunjatest
|
||||
VIKUNJA_DATABASE_SSLMODE: disable
|
||||
commands:
|
||||
- make integration-test
|
||||
depends_on: [ build ]
|
||||
when:
|
||||
event: [ push, tag, pull_request ]
|
||||
|
||||
---
|
||||
########
|
||||
# Build a release when pushing to master
|
||||
|
6
Makefile
6
Makefile
@ -64,7 +64,8 @@ clean:
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
VIKUNJA_SERVICE_ROOTPATH=$(shell pwd) go test $(GOFLAGS) -cover -coverprofile cover.out $(PACKAGES)
|
||||
# We run everything sequentially and not in parallel to prevent issues with real test databases
|
||||
VIKUNJA_SERVICE_ROOTPATH=$(shell pwd) go test $(GOFLAGS) -p 1 -cover -coverprofile cover.out $(PACKAGES)
|
||||
|
||||
.PHONY: test-coverage
|
||||
test-coverage: test
|
||||
@ -72,7 +73,8 @@ test-coverage: test
|
||||
|
||||
.PHONY: integration-test
|
||||
integration-test:
|
||||
VIKUNJA_SERVICE_ROOTPATH=$(shell pwd) go test $(GOFLAGS) code.vikunja.io/api/pkg/integrations
|
||||
# We run everything sequentially and not in parallel to prevent issues with real test databases
|
||||
VIKUNJA_SERVICE_ROOTPATH=$(shell pwd) go test $(GOFLAGS) -p 1 code.vikunja.io/api/pkg/integrations
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
|
@ -30,7 +30,7 @@ service:
|
||||
timezone: GMT
|
||||
|
||||
database:
|
||||
# Database type to use. Supported types are mysql and sqlite.
|
||||
# Database type to use. Supported types are mysql, postgres and sqlite.
|
||||
type: "sqlite"
|
||||
# Database user which is used to connect to the database.
|
||||
user: "vikunja"
|
||||
@ -42,12 +42,15 @@ database:
|
||||
database: "vikunja"
|
||||
# When using sqlite, this is the path where to store the data
|
||||
Path: "./vikunja.db"
|
||||
# Sets the max open connections to the database. Only used when using mysql.
|
||||
# Sets the max open connections to the database. Only used when using mysql and postgres.
|
||||
maxopenconnections: 100
|
||||
# Sets the maximum number of idle connections to the db.
|
||||
maxidleconnections: 50
|
||||
# The maximum lifetime of a single db connection in miliseconds.
|
||||
maxconnectionlifetime: 10000
|
||||
# Secure connection mode. Only used with postgres.
|
||||
# (see https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection_String_Parameters)
|
||||
sslmode: disable
|
||||
|
||||
cache:
|
||||
# If cache is enabled or not
|
||||
|
@ -29,6 +29,24 @@ To restore it, simply pipe it back into the `mysql` command:
|
||||
mysql -u <user> -p -h <db-host> <database> < vkunja-backup.sql
|
||||
{{< /highlight >}}
|
||||
|
||||
## PostgreSQL
|
||||
|
||||
To create a backup from PostgreSQL use the `pg_dump` command:
|
||||
|
||||
{{< highlight bash >}}
|
||||
pg_dump -U <user> -h <db-host> <database> > vikunja-backup.sql
|
||||
{{< /highlight >}}
|
||||
|
||||
You might be prompted for the password of the database user.
|
||||
|
||||
To restore it, simply pipe it back into the `psql` command:
|
||||
|
||||
{{< highlight bash >}}
|
||||
psql -U <user> -h <db-host> <database> < vikunja-backup.sql
|
||||
{{< /highlight >}}
|
||||
|
||||
For more information, please visit the [relevant PostgreSQL documentation](https://www.postgresql.org/docs/12/backup-dump.html).
|
||||
|
||||
## SQLite
|
||||
|
||||
To backup sqllite databases, it is enough to copy the database elsewhere.
|
||||
|
@ -73,7 +73,7 @@ service:
|
||||
timezone: GMT
|
||||
|
||||
database:
|
||||
# Database type to use. Supported types are mysql and sqlite.
|
||||
# Database type to use. Supported types are mysql, postgres and sqlite.
|
||||
type: "sqlite"
|
||||
# Database user which is used to connect to the database.
|
||||
user: "vikunja"
|
||||
@ -85,12 +85,15 @@ database:
|
||||
database: "vikunja"
|
||||
# When using sqlite, this is the path where to store the data
|
||||
Path: "./vikunja.db"
|
||||
# Sets the max open connections to the database. Only used when using mysql.
|
||||
# Sets the max open connections to the database. Only used when using mysql and postgres.
|
||||
maxopenconnections: 100
|
||||
# Sets the maximum number of idle connections to the db.
|
||||
maxidleconnections: 50
|
||||
# The maximum lifetime of a single db connection in miliseconds.
|
||||
maxconnectionlifetime: 10000
|
||||
# Secure connection mode. Only used with postgres.
|
||||
# (see https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection_String_Parameters)
|
||||
sslmode: disable
|
||||
|
||||
cache:
|
||||
# If cache is enabled or not
|
||||
|
@ -55,6 +55,7 @@ After=network.target
|
||||
# Depending on how you configured Vikunja, you may want to uncomment these:
|
||||
#Requires=mysql.service
|
||||
#Requires=mariadb.service
|
||||
#Requires=postgresql.service
|
||||
#Requires=redis.service
|
||||
|
||||
[Service]
|
||||
|
5
go.mod
5
go.mod
@ -35,6 +35,7 @@ require (
|
||||
github.com/go-openapi/spec v0.19.4 // indirect
|
||||
github.com/go-redis/redis v6.15.2+incompatible
|
||||
github.com/go-sql-driver/mysql v1.5.0
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.1.1
|
||||
github.com/go-xorm/core v0.6.2 // indirect
|
||||
github.com/go-xorm/xorm v0.7.9 // indirect
|
||||
github.com/golang/protobuf v1.3.2 // indirect
|
||||
@ -45,9 +46,9 @@ require (
|
||||
github.com/labstack/echo/v4 v4.1.14
|
||||
github.com/labstack/gommon v0.3.0
|
||||
github.com/laurent22/ical-go v0.1.1-0.20181107184520-7e5d6ade8eef
|
||||
github.com/lib/pq v1.3.0
|
||||
github.com/mailru/easyjson v0.7.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/mattn/go-oci8 v0.0.0-20181130072307-052f5d97b9b6 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.4 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||
@ -74,8 +75,6 @@ require (
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/d4l3k/messagediff.v1 v1.2.1
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/testfixtures.v2 v2.5.3
|
||||
gopkg.in/yaml.v2 v2.2.7 // indirect
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a
|
||||
src.techknowlogick.com/xgo v0.0.0-20190507142556-a5b29ecb0ff4
|
||||
src.techknowlogick.com/xormigrate v1.1.0
|
||||
|
15
go.sum
15
go.sum
@ -1,5 +1,4 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
|
||||
@ -54,10 +53,9 @@ github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkE
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f h1:WH0w/R4Yoey+04HhFxqZ6VX6I0d7RMyw5aXQ9UTvQPs=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e h1:LzwWXEScfcTu7vUZNlDDWDARoSGEtvlDKK2BYHowNeE=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
@ -110,6 +108,8 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.1.1 h1:SBIfzULODQQ7JV6AD931MvAz8pnkk6QCfMIcoOBDaXQ=
|
||||
github.com/go-testfixtures/testfixtures/v3 v3.1.1/go.mod h1:RZctY24ixituGC73XlAV1gkCwYMVwiSwPm26MNlQIhE=
|
||||
github.com/go-xorm/core v0.6.2 h1:EJLcSxf336POJr670wKB55Mah9f93xzvGYzNRgnT8/Y=
|
||||
github.com/go-xorm/core v0.6.2/go.mod h1:bwPIfLdm/FzWgVUH8WPVlr+uJhscvNGFcaZKXsI3n2c=
|
||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
|
||||
@ -211,13 +211,11 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-oci8 v0.0.0-20181115070430-6eefff3c767c/go.mod h1:/M9VLO+lUPmxvoOK2PfWRZ8mTtB4q1Hy9lEGijv9Nr8=
|
||||
github.com/mattn/go-oci8 v0.0.0-20181130072307-052f5d97b9b6 h1:gheNi9lnffYyVyqQzJqY7lo+M3bCDVw5fLU/jSuCMhc=
|
||||
github.com/mattn/go-oci8 v0.0.0-20181130072307-052f5d97b9b6/go.mod h1:/M9VLO+lUPmxvoOK2PfWRZ8mTtB4q1Hy9lEGijv9Nr8=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
@ -290,6 +288,8 @@ github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmq
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@ -330,7 +330,6 @@ github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wK
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@ -454,8 +453,6 @@ gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
|
||||
gopkg.in/testfixtures.v2 v2.5.3 h1:P8gDACSLJGxutzBqbzvfiXYgmQ2s00LIr4uAvWBCPAg=
|
||||
gopkg.in/testfixtures.v2 v2.5.3/go.mod h1:rGPtsOtPcZhs7AsHYf1WmufW1hEsM6DXdLrYz60nrQQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
|
@ -55,6 +55,7 @@ const (
|
||||
DatabaseMaxOpenConnections Key = `database.maxopenconnections`
|
||||
DatabaseMaxIdleConnections Key = `database.maxidleconnections`
|
||||
DatabaseMaxConnectionLifetime Key = `database.maxconnectionlifetime`
|
||||
DatabaseSslMode Key = `database.sslmode`
|
||||
|
||||
CacheEnabled Key = `cache.enabled`
|
||||
CacheType Key = `cache.type`
|
||||
@ -181,6 +182,7 @@ func InitDefaultConfig() {
|
||||
DatabaseMaxOpenConnections.setDefault(100)
|
||||
DatabaseMaxIdleConnections.setDefault(50)
|
||||
DatabaseMaxConnectionLifetime.setDefault(10000)
|
||||
DatabaseSslMode.setDefault("disable")
|
||||
|
||||
// Cacher
|
||||
CacheEnabled.setDefault(false)
|
||||
|
48
pkg/db/db.go
48
pkg/db/db.go
@ -21,7 +21,9 @@ import (
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"xorm.io/core"
|
||||
"xorm.io/xorm"
|
||||
@ -29,6 +31,7 @@ import (
|
||||
xrc "gitea.com/xorm/xorm-redis-cache"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql" // Because.
|
||||
_ "github.com/lib/pq" // Because.
|
||||
_ "github.com/mattn/go-sqlite3" // Because.
|
||||
)
|
||||
|
||||
@ -53,6 +56,11 @@ func CreateDBEngine() (engine *xorm.Engine, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else if config.DatabaseType.GetString() == "postgres" {
|
||||
engine, err = initPostgresEngine()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Otherwise use sqlite
|
||||
engine, err = initSqliteEngine()
|
||||
@ -113,6 +121,46 @@ func initMysqlEngine() (engine *xorm.Engine, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// parsePostgreSQLHostPort parses given input in various forms defined in
|
||||
// https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
|
||||
// and returns proper host and port number.
|
||||
func parsePostgreSQLHostPort(info string) (string, string) {
|
||||
host, port := "127.0.0.1", "5432"
|
||||
if strings.Contains(info, ":") && !strings.HasSuffix(info, "]") {
|
||||
idx := strings.LastIndex(info, ":")
|
||||
host = info[:idx]
|
||||
port = info[idx+1:]
|
||||
} else if len(info) > 0 {
|
||||
host = info
|
||||
}
|
||||
return host, port
|
||||
}
|
||||
|
||||
func initPostgresEngine() (engine *xorm.Engine, err error) {
|
||||
host, port := parsePostgreSQLHostPort(config.DatabaseHost.GetString())
|
||||
connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=%s",
|
||||
host,
|
||||
port,
|
||||
url.PathEscape(config.DatabaseUser.GetString()),
|
||||
url.PathEscape(config.DatabasePassword.GetString()),
|
||||
config.DatabaseDatabase.GetString(),
|
||||
config.DatabaseSslMode.GetString(),
|
||||
)
|
||||
|
||||
engine, err = xorm.NewEngine("postgres", connStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
engine.SetMaxOpenConns(config.DatabaseMaxOpenConnections.GetInt())
|
||||
engine.SetMaxIdleConns(config.DatabaseMaxIdleConnections.GetInt())
|
||||
max, err := time.ParseDuration(strconv.Itoa(config.DatabaseMaxConnectionLifetime.GetInt()) + `ms`)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
engine.SetConnMaxLifetime(max)
|
||||
return
|
||||
}
|
||||
|
||||
func initSqliteEngine() (engine *xorm.Engine, err error) {
|
||||
path := config.DatabasePath.GetString()
|
||||
if path == "" {
|
||||
|
@ -19,41 +19,92 @@ package db
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"fmt"
|
||||
"github.com/go-testfixtures/testfixtures/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/testfixtures.v2"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var fixtures *testfixtures.Context
|
||||
var fixtures *testfixtures.Loader
|
||||
|
||||
// InitFixtures initialize test fixtures for a test database
|
||||
func InitFixtures(tablenames ...string) (err error) {
|
||||
|
||||
var helper testfixtures.Helper = &testfixtures.SQLite{}
|
||||
if config.DatabaseType.GetString() == "mysql" {
|
||||
helper = &testfixtures.MySQL{}
|
||||
}
|
||||
var testfiles func(loader *testfixtures.Loader) error
|
||||
dir := filepath.Join(config.ServiceRootpath.GetString(), "pkg", "db", "fixtures")
|
||||
|
||||
testfixtures.SkipDatabaseNameCheck(true)
|
||||
|
||||
// If fixture table names are specified, load them
|
||||
// Otherwise, load all fixtures
|
||||
if len(tablenames) > 0 {
|
||||
for i, name := range tablenames {
|
||||
tablenames[i] = filepath.Join(dir, name+".yml")
|
||||
}
|
||||
fixtures, err = testfixtures.NewFiles(x.DB().DB, helper, tablenames...)
|
||||
testfiles = testfixtures.Files(tablenames...)
|
||||
} else {
|
||||
fixtures, err = testfixtures.NewFolder(x.DB().DB, helper, dir)
|
||||
testfiles = testfixtures.Directory(dir)
|
||||
}
|
||||
|
||||
loaderOptions := []func(loader *testfixtures.Loader) error{
|
||||
testfixtures.Database(x.DB().DB),
|
||||
testfixtures.Dialect(config.DatabaseType.GetString()),
|
||||
testfixtures.DangerousSkipTestDatabaseCheck(),
|
||||
testfiles,
|
||||
}
|
||||
|
||||
if config.DatabaseType.GetString() == "postgres" {
|
||||
loaderOptions = append(loaderOptions, testfixtures.SkipResetSequences())
|
||||
}
|
||||
|
||||
fixtures, err = testfixtures.New(loaderOptions...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// LoadFixtures load fixtures for a test database
|
||||
func LoadFixtures() error {
|
||||
return fixtures.Load()
|
||||
err := fixtures.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copied from https://github.com/go-gitea/gitea/blob/master/models/test_fixtures.go#L39
|
||||
// Now if we're running postgres we need to tell it to update the sequences
|
||||
if x.Dialect().DriverName() == "postgres" {
|
||||
results, err := x.QueryString(`SELECT 'SELECT SETVAL(' ||
|
||||
quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
|
||||
', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
|
||||
quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';'
|
||||
FROM pg_class AS S,
|
||||
pg_depend AS D,
|
||||
pg_class AS T,
|
||||
pg_attribute AS C,
|
||||
pg_tables AS PGT
|
||||
WHERE S.relkind = 'S'
|
||||
AND S.oid = D.objid
|
||||
AND D.refobjid = T.oid
|
||||
AND D.refobjid = C.attrelid
|
||||
AND D.refobjsubid = C.attnum
|
||||
AND T.relname = PGT.tablename
|
||||
ORDER BY S.relname;`)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to generate sequence update: %v\n", err)
|
||||
return err
|
||||
}
|
||||
for _, r := range results {
|
||||
for _, value := range r {
|
||||
_, err = x.Exec(value)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update sequence: %s Error: %v\n", value, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// LoadAndAssertFixtures loads all fixtures defined before and asserts they are correctly loaded
|
||||
|
@ -57,11 +57,11 @@ func TestCreate(t *testing.T) {
|
||||
content: []byte("testfile"),
|
||||
}
|
||||
ta := &testauth{id: 1}
|
||||
_, err := Create(tf, "testfile", 100, ta)
|
||||
createdFile, err := Create(tf, "testfile", 100, ta)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check the file was created correctly
|
||||
file := &File{ID: 2}
|
||||
file := &File{ID: createdFile.ID}
|
||||
err = file.LoadFileMetaByID()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(1), file.CreatedByID)
|
||||
|
@ -114,6 +114,11 @@ func dropTableColum(x *xorm.Engine, tableName, col string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "postgres":
|
||||
_, err := x.Exec("ALTER TABLE " + tableName + " DROP COLUMN " + col)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
log.Fatal("Unknown db.")
|
||||
}
|
||||
@ -130,6 +135,11 @@ func modifyColumn(x *xorm.Engine, tableName, col, newDefinition string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "postgres":
|
||||
_, err := x.Exec("ALTER TABLE " + tableName + " ALTER COLUMN " + col + " " + newDefinition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
log.Fatal("Unknown db.")
|
||||
}
|
||||
|
@ -154,8 +154,10 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, res
|
||||
// Because of this whole thing, we need this extra switch here to only group by Task IDs if needed.
|
||||
// Probably not the most ideal solution.
|
||||
var groupBy = "labels.id,label_task.task_id"
|
||||
var selectStmt = "labels.*, label_task.task_id"
|
||||
if opts.GroupByLabelIDsOnly {
|
||||
groupBy = "labels.id"
|
||||
selectStmt = "labels.*"
|
||||
}
|
||||
|
||||
// Get all labels associated with these tasks
|
||||
@ -166,11 +168,12 @@ func getLabelsByTaskIDs(opts *LabelByTaskIDsOptions) (ls []*labelWithTaskID, res
|
||||
}
|
||||
|
||||
err = x.Table("labels").
|
||||
Select("labels.*, label_task.task_id").
|
||||
Select(selectStmt).
|
||||
Join("LEFT", "label_task", "label_task.label_id = labels.id").
|
||||
Where(cond).
|
||||
And("labels.title LIKE ?", "%"+opts.Search+"%").
|
||||
GroupBy(groupBy).
|
||||
OrderBy("labels.id ASC").
|
||||
Limit(getLimitFromPageIndex(opts.Page, opts.PerPage)).
|
||||
Find(&labels)
|
||||
if err != nil {
|
||||
|
@ -83,7 +83,6 @@ func TestLabel_ReadAll(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
TaskID: 1,
|
||||
Label: Label{
|
||||
ID: 4,
|
||||
Title: "Label #4 - visible via other task",
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
_ "github.com/go-sql-driver/mysql" // Because.
|
||||
_ "github.com/lib/pq" // Because.
|
||||
"xorm.io/xorm"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3" // Because.
|
||||
|
@ -86,7 +86,7 @@ func TestNamespace_Create(t *testing.T) {
|
||||
assert.Equal(t, "Dolor sit amet.", dummynamespace.Description)
|
||||
|
||||
// Try updating one with a nonexistant owner
|
||||
dummynamespace.Owner.ID = 94829838572
|
||||
dummynamespace.Owner.ID = 999999
|
||||
err = dummynamespace.Update()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, user.IsErrUserDoesNotExist(err))
|
||||
|
@ -19,6 +19,7 @@ package models
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/files"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -30,6 +31,7 @@ import (
|
||||
|
||||
func TestTaskAttachment_ReadOne(t *testing.T) {
|
||||
t.Run("Normal File", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
files.InitTestFileFixtures(t)
|
||||
ta := &TaskAttachment{
|
||||
ID: 1,
|
||||
@ -50,6 +52,7 @@ func TestTaskAttachment_ReadOne(t *testing.T) {
|
||||
assert.Equal(t, []byte("testfile1"), content)
|
||||
})
|
||||
t.Run("Nonexisting Attachment", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
files.InitTestFileFixtures(t)
|
||||
ta := &TaskAttachment{
|
||||
ID: 9999,
|
||||
@ -59,6 +62,7 @@ func TestTaskAttachment_ReadOne(t *testing.T) {
|
||||
assert.True(t, IsErrTaskAttachmentDoesNotExist(err))
|
||||
})
|
||||
t.Run("Existing Attachment, Nonexisting File", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
files.InitTestFileFixtures(t)
|
||||
ta := &TaskAttachment{
|
||||
ID: 2,
|
||||
@ -88,6 +92,7 @@ func (t *testfile) Close() error {
|
||||
}
|
||||
|
||||
func TestTaskAttachment_NewAttachment(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
files.InitTestFileFixtures(t)
|
||||
// Assert the file is being stored correctly
|
||||
ta := TaskAttachment{
|
||||
@ -118,16 +123,18 @@ func TestTaskAttachment_NewAttachment(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTaskAttachment_ReadAll(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
files.InitTestFileFixtures(t)
|
||||
ta := &TaskAttachment{TaskID: 1}
|
||||
as, _, _, err := ta.ReadAll(&user.User{ID: 1}, "", 0, 50)
|
||||
attachments, _ := as.([]*TaskAttachment)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, attachments, 3)
|
||||
assert.Len(t, attachments, 2)
|
||||
assert.Equal(t, "test", attachments[0].File.Name)
|
||||
}
|
||||
|
||||
func TestTaskAttachment_Delete(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
files.InitTestFileFixtures(t)
|
||||
t.Run("Normal", func(t *testing.T) {
|
||||
ta := &TaskAttachment{ID: 1}
|
||||
@ -156,12 +163,14 @@ func TestTaskAttachment_Rights(t *testing.T) {
|
||||
u := &user.User{ID: 1}
|
||||
t.Run("Can Read", func(t *testing.T) {
|
||||
t.Run("Allowed", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
ta := &TaskAttachment{TaskID: 1}
|
||||
can, err := ta.CanRead(u)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, can)
|
||||
})
|
||||
t.Run("Forbidden", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
ta := &TaskAttachment{TaskID: 14}
|
||||
can, err := ta.CanRead(u)
|
||||
assert.NoError(t, err)
|
||||
@ -170,18 +179,21 @@ func TestTaskAttachment_Rights(t *testing.T) {
|
||||
})
|
||||
t.Run("Can Delete", func(t *testing.T) {
|
||||
t.Run("Allowed", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
ta := &TaskAttachment{TaskID: 1}
|
||||
can, err := ta.CanDelete(u)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, can)
|
||||
})
|
||||
t.Run("Forbidden, no access", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
ta := &TaskAttachment{TaskID: 14}
|
||||
can, err := ta.CanDelete(u)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("Forbidden, shared read only", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
ta := &TaskAttachment{TaskID: 15}
|
||||
can, err := ta.CanDelete(u)
|
||||
assert.NoError(t, err)
|
||||
@ -190,18 +202,21 @@ func TestTaskAttachment_Rights(t *testing.T) {
|
||||
})
|
||||
t.Run("Can Create", func(t *testing.T) {
|
||||
t.Run("Allowed", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
ta := &TaskAttachment{TaskID: 1}
|
||||
can, err := ta.CanCreate(u)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, can)
|
||||
})
|
||||
t.Run("Forbidden, no access", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
ta := &TaskAttachment{TaskID: 14}
|
||||
can, err := ta.CanCreate(u)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, can)
|
||||
})
|
||||
t.Run("Forbidden, shared read only", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
ta := &TaskAttachment{TaskID: 15}
|
||||
can, err := ta.CanCreate(u)
|
||||
assert.NoError(t, err)
|
||||
|
@ -24,61 +24,60 @@ import (
|
||||
)
|
||||
|
||||
func TestTeamMember_Create(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
|
||||
// Dummy team member
|
||||
dummyteammember := TeamMember{
|
||||
TeamID: 1,
|
||||
Username: "user3",
|
||||
doer := &user.User{
|
||||
ID: 1,
|
||||
}
|
||||
|
||||
// Doer
|
||||
doer, err := user.GetUserByID(1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Insert a new team member
|
||||
allowed, _ := dummyteammember.CanCreate(doer)
|
||||
assert.True(t, allowed)
|
||||
err = dummyteammember.Create(doer)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Check he's in there
|
||||
team := Team{ID: 1}
|
||||
err = team.ReadOne()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(team.Members))
|
||||
|
||||
// Try inserting a user twice
|
||||
err = dummyteammember.Create(doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrUserIsMemberOfTeam(err))
|
||||
|
||||
// Delete it
|
||||
allowed, _ = dummyteammember.CanDelete(doer)
|
||||
assert.True(t, allowed)
|
||||
err = dummyteammember.Delete()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Delete the other one
|
||||
tm := TeamMember{TeamID: 1, Username: "user2"}
|
||||
err = tm.Delete()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Try deleting the last one
|
||||
tm = TeamMember{TeamID: 1, Username: "user1"}
|
||||
err = tm.Delete()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrCannotDeleteLastTeamMember(err))
|
||||
|
||||
// Try inserting a user which does not exist
|
||||
dummyteammember.Username = "user9484"
|
||||
err = dummyteammember.Create(doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, user.IsErrUserDoesNotExist(err))
|
||||
|
||||
// Try adding a user to a team which does not exist
|
||||
tm = TeamMember{TeamID: 94824, Username: "user1"}
|
||||
err = tm.Create(doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
tm := &TeamMember{
|
||||
TeamID: 1,
|
||||
Username: "user3",
|
||||
}
|
||||
err := tm.Create(doer)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("already existing", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
tm := &TeamMember{
|
||||
TeamID: 1,
|
||||
Username: "user1",
|
||||
}
|
||||
err := tm.Create(doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrUserIsMemberOfTeam(err))
|
||||
})
|
||||
t.Run("nonexisting user", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
tm := &TeamMember{
|
||||
TeamID: 1,
|
||||
Username: "nonexistinguser",
|
||||
}
|
||||
err := tm.Create(doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, user.IsErrUserDoesNotExist(err))
|
||||
})
|
||||
t.Run("nonexisting team", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
tm := &TeamMember{
|
||||
TeamID: 9999999,
|
||||
Username: "user1",
|
||||
}
|
||||
err := tm.Create(doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
})
|
||||
}
|
||||
|
||||
func TestTeamMember_Delete(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
tm := &TeamMember{
|
||||
TeamID: 1,
|
||||
Username: "user1",
|
||||
}
|
||||
err := tm.Delete()
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ type TeamMember struct {
|
||||
// Used under the hood to manage team members
|
||||
UserID int64 `xorm:"int(11) not null INDEX" json:"-"`
|
||||
// Whether or not the member is an admin of the team. See the docs for more about what a team admin can do
|
||||
Admin bool `xorm:"tinyint(1) INDEX null" json:"admin"`
|
||||
Admin bool `xorm:"null" json:"admin"`
|
||||
|
||||
// A timestamp when this relation was created. You cannot change this value.
|
||||
Created timeutil.TimeStamp `xorm:"created not null" json:"created"`
|
||||
@ -131,7 +131,7 @@ func addMoreInfoToTeams(teams []*Team) (err error) {
|
||||
}
|
||||
|
||||
// Get all owners and team members
|
||||
users := []*TeamUser{}
|
||||
users := make(map[int64]*TeamUser)
|
||||
err = x.Select("*").
|
||||
Table("users").
|
||||
Join("LEFT", "team_members", "team_members.user_id = users.id").
|
||||
@ -153,9 +153,15 @@ func addMoreInfoToTeams(teams []*Team) (err error) {
|
||||
continue
|
||||
}
|
||||
u.Email = ""
|
||||
teamMap[u.TeamID].CreatedBy = &u.User
|
||||
teamMap[u.TeamID].Members = append(teamMap[u.TeamID].Members, u)
|
||||
}
|
||||
|
||||
// We need to do this in a second loop as owners might not be the last ones in the list
|
||||
for _, team := range teamMap {
|
||||
if teamUser, has := users[team.CreatedByID]; has {
|
||||
team.CreatedBy = &teamUser.User
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"reflect"
|
||||
@ -24,79 +25,108 @@ import (
|
||||
)
|
||||
|
||||
func TestTeam_Create(t *testing.T) {
|
||||
//Dummyteam
|
||||
dummyteam := Team{
|
||||
Name: "Testteam293",
|
||||
Description: "Lorem Ispum",
|
||||
doer := &user.User{
|
||||
ID: 1,
|
||||
Username: "user1",
|
||||
}
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
team := &Team{
|
||||
Name: "Testteam293",
|
||||
Description: "Lorem Ispum",
|
||||
}
|
||||
err := team.Create(doer)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("empty name", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
team := &Team{}
|
||||
err := team.Create(doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamNameCannotBeEmpty(err))
|
||||
})
|
||||
}
|
||||
|
||||
// Doer
|
||||
doer, err := user.GetUserByID(1)
|
||||
assert.NoError(t, err)
|
||||
func TestTeam_ReadOne(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
team := &Team{ID: 1}
|
||||
err := team.ReadOne()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "testteam1", team.Name)
|
||||
assert.Equal(t, "Lorem Ipsum", team.Description)
|
||||
assert.Equal(t, int64(1), team.CreatedBy.ID)
|
||||
assert.Equal(t, int64(1), team.CreatedByID)
|
||||
})
|
||||
t.Run("invalid id", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
team := &Team{ID: -1}
|
||||
err := team.ReadOne()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
})
|
||||
t.Run("nonexisting", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
team := &Team{ID: 99999}
|
||||
err := team.ReadOne()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
})
|
||||
}
|
||||
|
||||
// Insert it
|
||||
allowed, _ := dummyteam.CanCreate(doer)
|
||||
assert.True(t, allowed)
|
||||
err = dummyteam.Create(doer)
|
||||
assert.NoError(t, err)
|
||||
func TestTeam_ReadAll(t *testing.T) {
|
||||
doer := &user.User{ID: 1}
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
team := &Team{}
|
||||
ts, _, _, err := team.ReadAll(doer, "", 1, 50)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, reflect.TypeOf(ts).Kind(), reflect.Slice)
|
||||
s := reflect.ValueOf(ts)
|
||||
assert.Equal(t, 8, s.Len())
|
||||
})
|
||||
}
|
||||
|
||||
// Check if it was inserted and we're admin
|
||||
tm := Team{ID: dummyteam.ID}
|
||||
err = tm.ReadOne()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(tm.Members))
|
||||
assert.Equal(t, doer.ID, tm.Members[0].User.ID)
|
||||
assert.True(t, tm.Members[0].Admin)
|
||||
allowed, _ = dummyteam.CanRead(doer)
|
||||
assert.True(t, allowed)
|
||||
func TestTeam_Update(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
team := &Team{
|
||||
ID: 1,
|
||||
Name: "SomethingNew",
|
||||
}
|
||||
err := team.Update()
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("empty name", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
team := &Team{
|
||||
ID: 1,
|
||||
Name: "",
|
||||
}
|
||||
err := team.Update()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamNameCannotBeEmpty(err))
|
||||
})
|
||||
t.Run("nonexisting", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
team := &Team{
|
||||
ID: 9999,
|
||||
Name: "SomethingNew",
|
||||
}
|
||||
err := team.Update()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
})
|
||||
}
|
||||
|
||||
// Try getting a team with an ID < 0
|
||||
_, err = GetTeamByID(-1)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
|
||||
// Get all teams the user is part of
|
||||
ts, _, _, err := tm.ReadAll(doer, "", 1, 50)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, reflect.TypeOf(ts).Kind(), reflect.Slice)
|
||||
s := reflect.ValueOf(ts)
|
||||
assert.Equal(t, 9, s.Len())
|
||||
|
||||
// Check inserting it with an empty name
|
||||
dummyteam.Name = ""
|
||||
err = dummyteam.Create(doer)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamNameCannotBeEmpty(err))
|
||||
|
||||
// update it (still no name, should fail)
|
||||
allowed, _ = dummyteam.CanUpdate(doer)
|
||||
assert.True(t, allowed)
|
||||
err = dummyteam.Update()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamNameCannotBeEmpty(err))
|
||||
|
||||
// Update it, this time with a name
|
||||
dummyteam.Name = "Lorem"
|
||||
err = dummyteam.Update()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Delete it
|
||||
allowed, err = dummyteam.CanDelete(doer)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, allowed)
|
||||
err = dummyteam.Delete()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Try deleting a (now) nonexistant team
|
||||
allowed, err = dummyteam.CanDelete(doer)
|
||||
assert.False(t, allowed)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
|
||||
// Try updating the (now) nonexistant team
|
||||
err = dummyteam.Update()
|
||||
assert.Error(t, err)
|
||||
assert.True(t, IsErrTeamDoesNotExist(err))
|
||||
func TestTeam_Delete(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
team := &Team{
|
||||
ID: 1,
|
||||
}
|
||||
err := team.Delete()
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIsErrInvalidRight(t *testing.T) {
|
||||
|
@ -23,7 +23,7 @@ _testmain.go
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
# SQLite databases
|
||||
*.sqlite3
|
||||
|
||||
.env
|
||||
/testfixtures
|
||||
/dist
|
41
vendor/github.com/go-testfixtures/testfixtures/v3/.goreleaser.yml
generated
vendored
Normal file
41
vendor/github.com/go-testfixtures/testfixtures/v3/.goreleaser.yml
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
build:
|
||||
binary: testfixtures
|
||||
main: ./cmd/testfixtures
|
||||
goos:
|
||||
- windows
|
||||
- darwin
|
||||
- linux
|
||||
goarch:
|
||||
- 386
|
||||
- amd64
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
flags:
|
||||
- -tags=sqlite
|
||||
|
||||
archives:
|
||||
- name_template: "{{.Binary}}_{{.Os}}_{{.Arch}}"
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
|
||||
release:
|
||||
draft: true
|
||||
|
||||
snapshot:
|
||||
name_template: "{{.Tag}}"
|
||||
|
||||
checksum:
|
||||
name_template: "testfixtures_checksums.txt"
|
||||
|
||||
nfpms:
|
||||
- vendor: testfixtures
|
||||
homepage: https://github.com/go-testfixtures/testfixtures
|
||||
maintainer: Andrey Nering <andrey.nering@gmail.com>
|
||||
description: Ruby on Rails like test fixtures for Go.
|
||||
license: MIT
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
file_name_template: "{{.ProjectName}}_{{.Os}}_{{.Arch}}"
|
@ -2,4 +2,3 @@ PG_CONN_STRING="user=postgres dbname=testfixtures_test sslmode=disable"
|
||||
MYSQL_CONN_STRING="root:@/testfixtures_test?multiStatements=true"
|
||||
SQLITE_CONN_STRING="testdb.sqlite3"
|
||||
SQLSERVER_CONN_STRING="server=localhost\SQLExpress;database=testfixtures_test;user id=sa;password=sqlserver;encrypt=disable"
|
||||
ORACLE_CONN_STRING="testfixtures/testfixtures@localhost/XE"
|
83
vendor/github.com/go-testfixtures/testfixtures/v3/CHANGELOG.md
generated
vendored
Normal file
83
vendor/github.com/go-testfixtures/testfixtures/v3/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
# Changelog
|
||||
|
||||
## v3.1.1 - 2019-01-11
|
||||
|
||||
- testfixtures now work with both `mssql` and `sqlserver` drivers.
|
||||
Note that [the `mssql` one is deprecated](https://github.com/denisenkom/go-mssqldb#deprecated),
|
||||
though. So try to migrate to `sqlserver` once possible.
|
||||
|
||||
## v3.1.0 - 2019-01-09
|
||||
|
||||
- Using `sqlserver` driver instead of the deprecated `mssql`
|
||||
([#58](https://github.com/go-testfixtures/testfixtures/pull/58)).
|
||||
|
||||
## v3.0.0 - 2019-12-26
|
||||
|
||||
### Breaking changes
|
||||
|
||||
- The import path changed from `gopkg.in/testfixtures.v2` to
|
||||
`github.com/go-testfixtures/testfixtures/v3`.
|
||||
- This package no longer support Oracle databases. This decision was
|
||||
taken because too few people actually used this package with Oracle and it
|
||||
was the most difficult to test (we didn't run on CI due the lack of an
|
||||
official Docker image, etc).
|
||||
- The public API was totally rewritten to be more flexible and ideomatic.
|
||||
It now uses functional options. It differs from v2, but should be easy
|
||||
enough to upgrade.
|
||||
- Some deprecated APIs from v2 were removed as well.
|
||||
- This now requires Go >= 1.13.
|
||||
|
||||
### New features
|
||||
|
||||
- We now have a CLI so you can easily use testfixtures to load a sample
|
||||
database from fixtures if you want.
|
||||
- Templating via [text/template](https://golang.org/pkg/text/template/)
|
||||
is now available. This allows some fancier use cases like generating data
|
||||
or specific columns dynamically.
|
||||
- It's now possible to choose which time zone to use when parsing timestamps
|
||||
from fixtures. The default is the same as before, whatever is set on
|
||||
`time.Local`.
|
||||
- Errors now use the new `%w` verb only available on Go >= 1.13.
|
||||
|
||||
### MISC
|
||||
|
||||
- Travis and AppVeyor are gone. We're using GitHub Actions exclusively now.
|
||||
The whole suite is ran inside Docker (with help of Docker Compose), so it's
|
||||
easy to run tests locally as well.
|
||||
|
||||
Check the new README for some examples!
|
||||
|
||||
## v2.6.0 - 2019-10-24
|
||||
|
||||
- Add support for TimescaleDB
|
||||
([#53](https://github.com/go-testfixtures/testfixtures/pull/53)).
|
||||
|
||||
## v2.5.3 - 2018-12-15
|
||||
|
||||
- Fixes related to use of foreign key pragmas on MySQL (#43).
|
||||
|
||||
## v2.5.2 - 2018-11-25
|
||||
|
||||
- This library now supports [Go Modules](https://github.com/golang/go/wiki/Modules);
|
||||
- Also allow `.yaml` (as an alternative to `.yml`) as the file extension (#42).
|
||||
|
||||
## v2.5.1 - 2018-11-04
|
||||
|
||||
- Allowing disabling reset of PostgreSQL sequences (#38).
|
||||
|
||||
## v2.5.0 - 2018-09-07
|
||||
|
||||
- Add public function DetectTestDatabase (#35, #36).
|
||||
|
||||
## v2.4.5 - 2018-07-07
|
||||
|
||||
- Fix for MySQL/MariaDB: ignoring views on operations that should be run only on tables (#33).
|
||||
|
||||
## v2.4.4 - 2018-07-02
|
||||
|
||||
- Fix for multiple schemas on Microsoft SQL Server (#29 and #30);
|
||||
- Configuring AppVeyor CI to also test for Microsoft SQL Server.
|
||||
|
||||
---
|
||||
|
||||
Sorry, we don't have changelog for older releases 😢.
|
9
vendor/github.com/go-testfixtures/testfixtures/v3/Dockerfile
generated
vendored
Normal file
9
vendor/github.com/go-testfixtures/testfixtures/v3/Dockerfile
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
FROM golang:1.13.5-alpine
|
||||
|
||||
RUN apk update
|
||||
RUN apk add alpine-sdk
|
||||
|
||||
WORKDIR /testfixtures
|
||||
COPY . .
|
||||
|
||||
RUN go mod download
|
464
vendor/github.com/go-testfixtures/testfixtures/v3/README.md
generated
vendored
Normal file
464
vendor/github.com/go-testfixtures/testfixtures/v3/README.md
generated
vendored
Normal file
@ -0,0 +1,464 @@
|
||||
# testfixtures
|
||||
|
||||
[![GoDoc](https://godoc.org/github.com/go-testfixtures/testfixtures?status.svg)][doc]
|
||||
|
||||
> ***Warning***: this package will wipe the database data before loading the
|
||||
fixtures! It is supposed to be used on a test database. Please, double check
|
||||
if you are running it against the correct database.
|
||||
|
||||
> **TIP**: There are options not described in this README page. It's
|
||||
> recommended that you also check [the documentation][doc].
|
||||
|
||||
Writing tests is hard, even more when you have to deal with an SQL database.
|
||||
This package aims to make writing functional tests for web apps written in
|
||||
Go easier.
|
||||
|
||||