Problem: no sources in database
All checks were successful
continuous-integration/drone/push Build is passing

Solution: add sources in database and tests
This commit is contained in:
Peter Stuifzand 2022-04-20 13:22:06 +02:00
parent 974f460f84
commit caaa069660
Signed by: peter
GPG Key ID: 374322D56E5209E8
6 changed files with 172 additions and 12 deletions

View File

@ -7,6 +7,17 @@ workspace:
base: /go base: /go
path: src/p83.nl/go/ekster path: src/p83.nl/go/ekster
services:
- name: redis
image: redis:5
- name: database
image: postgres:14
environment:
POSTGRES_DB: ekster_testing
POSTGRES_USER: postgres
POSTGRES_PASSWORD: simple
POSTGRES_HOST_AUTH_METHOD: trust
steps: steps:
- name: testing - name: testing
image: golang:1.18-alpine image: golang:1.18-alpine
@ -18,11 +29,9 @@ steps:
- go version - go version
- apk --no-cache add git - apk --no-cache add git
- go get -d -t ./... - go get -d -t ./...
- go install honnef.co/go/tools/cmd/staticcheck@latest - go build -buildvcs=false p83.nl/go/ekster/cmd/eksterd
- go build p83.nl/go/ekster/cmd/eksterd
- go vet ./... - go vet ./...
- go test -v ./... - go test -v ./...
- staticcheck ./...
- name: publish-personal - name: publish-personal
image: plugins/docker image: plugins/docker

View File

@ -0,0 +1,123 @@
/*
* Ekster is a microsub server
* Copyright (c) 2022 The Ekster authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"database/sql"
"log"
"net/http/httptest"
"os"
"testing"
"github.com/gomodule/redigo/redis"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)
type DatabaseSuite struct {
suite.Suite
URL string
Database *sql.DB
RedisURL string
Redis redis.Conn
}
func (s *DatabaseSuite) SetupSuite() {
db, err := sql.Open("postgres", s.URL)
if err != nil {
log.Fatal(err)
}
s.Database = db
conn, err := redis.Dial("tcp", s.RedisURL)
if err != nil {
log.Fatal(err)
}
s.Redis = conn
_, err = s.Redis.Do("SELECT", "1")
if err != nil {
log.Fatal(err)
}
}
func (s *DatabaseSuite) TearDownSuite() {
err := s.Database.Close()
if err != nil {
log.Fatal(err)
}
err = s.Redis.Close()
if err != nil {
log.Fatal(err)
}
}
type databaseSuite struct {
DatabaseSuite
}
func (d *databaseSuite) TestGetChannelFromAuthorization() {
_, err := d.Database.Exec(`truncate "sources", "channels", "feeds", "subscriptions","items"`)
assert.NoError(d.T(), err, "truncate sources, channels, feeds")
row := d.Database.QueryRow(`INSERT INTO "channels" (uid, name, created_at, updated_at) VALUES ('abcdef', 'Channel', now(), now()) RETURNING "id"`)
var id int
err = row.Scan(&id)
assert.NoError(d.T(), err, "insert channel")
_, err = d.Database.Exec(`INSERT INTO "sources" (channel_id, auth_code, created_at, updated_at) VALUES ($1, '1234', now(), now())`, id)
assert.NoError(d.T(), err, "insert sources")
// source_id found
r := httptest.NewRequest("POST", "/micropub?source_id=1234", nil)
c, err := getChannelFromAuthorization(r, d.Redis, d.Database)
assert.NoError(d.T(), err, "channel from source_id")
assert.Equal(d.T(), "abcdef", c, "channel uid found")
// source_id not found
r = httptest.NewRequest("POST", "/micropub?source_id=1111", nil)
c, err = getChannelFromAuthorization(r, d.Redis, d.Database)
assert.Error(d.T(), err, "channel from authorization header")
assert.Equal(d.T(), "", c, "channel uid found")
}
func TestDatabaseSuite(t *testing.T) {
if testing.Short() {
t.Skip("Skip test for database")
}
databaseURL := os.Getenv("DATABASE_TEST_URL")
if databaseURL == "" {
databaseURL = "host=database user=postgres password=simple dbname=ekster_testing sslmode=disable"
}
databaseSuite := &databaseSuite{
DatabaseSuite{
URL: databaseURL,
RedisURL: "redis:6379",
},
}
databaseURL = "postgres://postgres@database/ekster_testing?sslmode=disable&user=postgres&password=simple"
err := runMigrations(databaseURL)
if err != nil {
log.Fatal(err)
}
suite.Run(t, databaseSuite)
}

View File

@ -0,0 +1 @@
DROP TABLE "sources";

View File

@ -0,0 +1,20 @@
BEGIN;
CREATE OR REPLACE FUNCTION update_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ language 'plpgsql';
COMMIT;
CREATE TABLE "sources" (
"id" int primary key generated always as identity,
"channel_id" int not null,
"auth_code" varchar(64) not null,
"created_at" timestamp DEFAULT current_timestamp,
"updated_at" timestamp DEFAULT current_timestamp
);
CREATE TRIGGER sources_update_timestamp BEFORE INSERT OR UPDATE ON "sources"
FOR EACH ROW EXECUTE PROCEDURE update_timestamp();

View File

@ -167,8 +167,8 @@ func main() {
// } // }
// TODO(peter): automatically gather this information from login or otherwise // TODO(peter): automatically gather this information from login or otherwise
databaseURL := "postgres://postgres@database/ekster?sslmode=disable&user=postgres&password=simple"
err := runMigrations() err := runMigrations(databaseURL)
if err != nil { if err != nil {
log.Fatalf("Error with migrations: %s", err) log.Fatalf("Error with migrations: %s", err)
} }
@ -205,12 +205,12 @@ func (l Log) Verbose() bool {
return false return false
} }
func runMigrations() error { func runMigrations(databaseURL string) error {
d, err := iofs.New(migrations, "db/migrations") d, err := iofs.New(migrations, "db/migrations")
if err != nil { if err != nil {
return err return err
} }
m, err := migrate.NewWithSourceInstance("iofs", d, "postgres://postgres@database/ekster?sslmode=disable&user=postgres&password=simple") m, err := migrate.NewWithSourceInstance("iofs", d, databaseURL)
if err != nil { if err != nil {
return err return err
} }

View File

@ -20,6 +20,7 @@ package main
import ( import (
"crypto/sha1" "crypto/sha1"
"database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log" "log"
@ -65,7 +66,7 @@ func (h *micropubHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodPost { if r.Method == http.MethodPost {
var channel string var channel string
channel, err = getChannelFromAuthorization(r, conn) channel, err = getChannelFromAuthorization(r, conn, h.Backend.database)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
http.Error(w, "unauthorized", http.StatusUnauthorized) http.Error(w, "unauthorized", http.StatusUnauthorized)
@ -165,15 +166,21 @@ func parseIncomingItem(r *http.Request) (*microsub.Item, error) {
return nil, fmt.Errorf("content-type %q is not supported", contentType) return nil, fmt.Errorf("content-type %q is not supported", contentType)
} }
func getChannelFromAuthorization(r *http.Request, conn redis.Conn) (string, error) { func getChannelFromAuthorization(r *http.Request, conn redis.Conn, database *sql.DB) (string, error) {
// backward compatible // backward compatible
sourceID := r.URL.Query().Get("source_id") sourceID := r.URL.Query().Get("source_id")
if sourceID != "" { if sourceID != "" {
channel, err := redis.String(conn.Do("HGET", "sources", sourceID)) row := database.QueryRow(`
if err != nil { SELECT c.uid
FROM "sources" AS "s"
INNER JOIN "channels" AS "c" ON s.channel_id = c.id
WHERE "auth_code" = $1
`, sourceID)
var channel string
if err := row.Scan(&channel); err == sql.ErrNoRows {
return "", errors.Wrapf(err, "could not get channel for sourceID: %s", sourceID) return "", errors.Wrapf(err, "could not get channel for sourceID: %s", sourceID)
} }
return channel, nil return channel, nil
} }