WIP
test / test (push) Failing after 18s
Details
test / test (push) Failing after 18s
Details
This commit is contained in:
parent
62cd2f8f42
commit
e6fa834473
11
README.md
11
README.md
|
@ -10,8 +10,9 @@ gleam shell # Run an Erlang shell
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- [X] Handle link GET
|
- [x] Handle link GET
|
||||||
- [X] Handle link POST
|
- [x] Handle link POST
|
||||||
- [] Handle file POST
|
- [ ] Handle file POST
|
||||||
- [] Handle file GET
|
- [ ] Handle file GET
|
||||||
- [] Web Frontend
|
- [ ] Web Frontend
|
||||||
|
- [ ] fix config loading from env
|
||||||
|
|
|
@ -69,25 +69,15 @@ pub fn load_config_from_env() -> Config {
|
||||||
postgres_pool_size: 15,
|
postgres_pool_size: 15,
|
||||||
)
|
)
|
||||||
Config(
|
Config(
|
||||||
port: "PORT"
|
port: get_int_or("PORT", defaults.port),
|
||||||
|> get_int_or(defaults.port),
|
max_bytes: get_int_or("MAX_BYTES", defaults.max_bytes),
|
||||||
max_bytes: "MAX_BYTES"
|
url_root: get_string_or("URL_ROOT", defaults.url_root),
|
||||||
|> get_int_or(defaults.max_bytes),
|
secret_key_base: get_string_or("SECRET_KEY_BASE", defaults.secret_key_base),
|
||||||
url_root: "URL_ROOT"
|
postgres_host: get_string_or("PG_HOST", defaults.postgres_host),
|
||||||
|> get_string_or(defaults.url_root),
|
postgres_db: get_string_or("PG_DB", defaults.postgres_db),
|
||||||
secret_key_base: "SECRET_KEY_BASE"
|
postgres_user: get_string_or("PG_USER", defaults.postgres_user),
|
||||||
|> get_string_or(defaults.secret_key_base),
|
postgres_password: get_string_or("PG_PASSWORD", defaults.postgres_password),
|
||||||
postgres_host: "POSTGRES_HOST"
|
postgres_pool_size: get_int_or("PG_POOL_SIZE", defaults.postgres_pool_size),
|
||||||
|> get_string_or(defaults.postgres_host),
|
postgres_port: get_int_or("PG_PORT", defaults.postgres_port),
|
||||||
postgres_db: "POSTGRES_DB"
|
|
||||||
|> get_string_or(defaults.postgres_db),
|
|
||||||
postgres_user: "POSTGRES_USER"
|
|
||||||
|> get_string_or(defaults.postgres_user),
|
|
||||||
postgres_password: "POSTGRES_PASSWORD"
|
|
||||||
|> get_string_or(defaults.postgres_password),
|
|
||||||
postgres_pool_size: "POSTGRES_POOL_SIZE"
|
|
||||||
|> get_int_or(defaults.postgres_pool_size),
|
|
||||||
postgres_port: "POSTGRES_PORT"
|
|
||||||
|> get_int_or(defaults.postgres_port),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,10 @@ import ids/cuid
|
||||||
import wisp.{type Request, type Response}
|
import wisp.{type Request, type Response}
|
||||||
|
|
||||||
const schema = "
|
const schema = "
|
||||||
CREATE TABLE IF NOT EXISTS \"File\" (
|
CREATE TABLE IF NOT EXISTS \"file\" (
|
||||||
Cuid CHAR(25) PRIMARY KEY,
|
cuid CHAR(25) PRIMARY KEY,
|
||||||
Md5Hash CHAR(32)
|
md5hash CHAR(32),
|
||||||
FileName TEXT
|
fileName TEXT
|
||||||
);
|
);
|
||||||
"
|
"
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ pub fn prepare_database(ctx: Context) -> Result(Nil, pgo.QueryError) {
|
||||||
|
|
||||||
pub fn store(file: File, ctx: Context) -> Result(String, Nil) {
|
pub fn store(file: File, ctx: Context) -> Result(String, Nil) {
|
||||||
let sql =
|
let sql =
|
||||||
"INSERT INTO \"File\" (Cuid, Md5Hash) VALUES ($1, $2) RETURNING Cuid;"
|
"INSERT INTO \"file\" (cuid, md5hash) VALUES ($1, $2) RETURNING cuid;"
|
||||||
case
|
case
|
||||||
pgo.execute(
|
pgo.execute(
|
||||||
sql,
|
sql,
|
||||||
|
@ -45,7 +45,7 @@ pub fn store(file: File, ctx: Context) -> Result(String, Nil) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn retrieve(cuid: String, ctx: Context) -> Result(File, Nil) {
|
pub fn retrieve(cuid: String, ctx: Context) -> Result(File, Nil) {
|
||||||
let sql = "SELECT Cuid,Md5Hash,FileName FROM \"File\" WHERE Cuid = $1;"
|
let sql = "SELECT cuid,md5hash,filename FROM \"file\" WHERE cuid = $1;"
|
||||||
case
|
case
|
||||||
pgo.execute(
|
pgo.execute(
|
||||||
sql,
|
sql,
|
||||||
|
|
|
@ -10,9 +10,9 @@ import ids/cuid
|
||||||
import wisp.{type Request, type Response}
|
import wisp.{type Request, type Response}
|
||||||
|
|
||||||
const schema = "
|
const schema = "
|
||||||
CREATE TABLE IF NOT EXISTS \"Link\" (
|
CREATE TABLE IF NOT EXISTS \"link\" (
|
||||||
Cuid CHAR(25) PRIMARY KEY,
|
cuid CHAR(25) PRIMARY KEY,
|
||||||
TargetURL TEXT
|
targeturl TEXT
|
||||||
);
|
);
|
||||||
"
|
"
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ pub fn prepare_database(ctx: Context) -> Result(Nil, pgo.QueryError) {
|
||||||
|
|
||||||
pub fn store(link: Link, ctx: Context) -> Result(String, Nil) {
|
pub fn store(link: Link, ctx: Context) -> Result(String, Nil) {
|
||||||
let sql =
|
let sql =
|
||||||
"INSERT INTO \"Link\" (Cuid, TargetURL) VALUES ($1, $2) RETURNING Cuid;"
|
"INSERT INTO \"link\" (cuid, targeturl) VALUES ($1, $2) RETURNING cuid;"
|
||||||
case
|
case
|
||||||
pgo.execute(
|
pgo.execute(
|
||||||
sql,
|
sql,
|
||||||
|
@ -43,7 +43,7 @@ pub fn store(link: Link, ctx: Context) -> Result(String, Nil) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn retrieve(cuid: String, ctx: Context) -> Result(Link, Nil) {
|
pub fn retrieve(cuid: String, ctx: Context) -> Result(Link, Nil) {
|
||||||
let sql = "SELECT Cuid,TargetURL FROM \"Link\" WHERE Cuid = $1;"
|
let sql = "SELECT cuid,targeturl FROM \"link\" WHERE cuid = $1;"
|
||||||
case
|
case
|
||||||
pgo.execute(
|
pgo.execute(
|
||||||
sql,
|
sql,
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import app/config.{type Context, Config, Context}
|
import app/config.{type Context, Config, Context}
|
||||||
import app/web/file
|
import app/web/file
|
||||||
import gleam/dynamic
|
import gleam/dynamic
|
||||||
import gleam/option.{None, Some}
|
import gleam/option.{Some}
|
||||||
import gleam/pgo
|
import gleam/pgo.{type Returned}
|
||||||
import gleam/string
|
import gleam/string
|
||||||
import gleeunit/should
|
import gleeunit/should
|
||||||
|
import ids/cuid
|
||||||
|
|
||||||
const test_config = Config(
|
const test_config = Config(
|
||||||
port: 8080,
|
port: 8080,
|
||||||
|
@ -20,6 +21,7 @@ const test_config = Config(
|
||||||
)
|
)
|
||||||
|
|
||||||
fn with_context(testcase: fn(Context) -> t) -> t {
|
fn with_context(testcase: fn(Context) -> t) -> t {
|
||||||
|
let assert Ok(cuid) = cuid.start()
|
||||||
let ctx =
|
let ctx =
|
||||||
Context(
|
Context(
|
||||||
db: pgo.Config(
|
db: pgo.Config(
|
||||||
|
@ -33,6 +35,7 @@ fn with_context(testcase: fn(Context) -> t) -> t {
|
||||||
)
|
)
|
||||||
|> pgo.connect,
|
|> pgo.connect,
|
||||||
config: test_config,
|
config: test_config,
|
||||||
|
cuid: cuid,
|
||||||
)
|
)
|
||||||
|
|
||||||
case file.prepare_database(ctx) {
|
case file.prepare_database(ctx) {
|
||||||
|
@ -44,25 +47,41 @@ fn with_context(testcase: fn(Context) -> t) -> t {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
Error(e) -> {
|
Error(e) -> {
|
||||||
e |> string.inspect |> panic
|
panic as string.inspect(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn null_test() {
|
fn cleanup() {
|
||||||
use ctx <- with_context()
|
use ctx <- with_context()
|
||||||
|
let assert Ok(_) =
|
||||||
|
pgo.execute("DROP TABLE file;", ctx.db, [], dynamic.dynamic)
|
||||||
|
}
|
||||||
|
|
||||||
pgo.execute(
|
pub fn prepare_database_test() {
|
||||||
"select $1",
|
use ctx <- with_context()
|
||||||
ctx.db,
|
let res =
|
||||||
[pgo.null()],
|
pgo.execute(
|
||||||
dynamic.element(0, dynamic.optional(dynamic.int)),
|
"SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'file';",
|
||||||
)
|
ctx.db,
|
||||||
|> should.equal(Ok(pgo.Returned(count: 1, rows: [None])))
|
[],
|
||||||
|
dynamic.tuple2(dynamic.string, dynamic.string),
|
||||||
|
)
|
||||||
|
|
||||||
|
res
|
||||||
|
|> should.be_ok
|
||||||
|
|> fn(x: Returned(#(String, String))) { x.rows }
|
||||||
|
|> should.equal([
|
||||||
|
#("cuid", "character"),
|
||||||
|
#("md5hash", "character"),
|
||||||
|
#("filename", "text"),
|
||||||
|
])
|
||||||
|
|
||||||
|
cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_to_path_test() {
|
pub fn hash_to_path_test() {
|
||||||
"f7b478961451483b8c251c788750d5ef"
|
"f7b478961451483b8c251c788750d5ef"
|
||||||
|> file.hash_to_path
|
|> file.hash_to_path
|
||||||
|> should.equal(["f7", "b4", "78961451483b8c251c788750d5ef"])
|
|> should.equal("/f7/b4/78961451483b8c251c788750d5ef")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import app/config.{type Context, Config, Context}
|
import app/config.{type Context, Config, Context}
|
||||||
import app/web/link.{Link}
|
import app/web/link.{Link}
|
||||||
import gleam/dynamic
|
import gleam/dynamic
|
||||||
import gleam/option.{None, Some}
|
import gleam/option.{Some}
|
||||||
import gleam/pgo
|
import gleam/pgo.{type Returned}
|
||||||
import gleam/result
|
|
||||||
import gleam/string
|
import gleam/string
|
||||||
import gleam/uri
|
import gleam/uri
|
||||||
import gleeunit/should
|
import gleeunit/should
|
||||||
import ids/nanoid
|
import ids/cuid
|
||||||
|
|
||||||
const test_config = Config(
|
const test_config = Config(
|
||||||
port: 8080,
|
port: 8080,
|
||||||
|
@ -23,6 +22,7 @@ const test_config = Config(
|
||||||
)
|
)
|
||||||
|
|
||||||
fn with_context(testcase: fn(Context) -> t) -> t {
|
fn with_context(testcase: fn(Context) -> t) -> t {
|
||||||
|
let assert Ok(cuid) = cuid.start()
|
||||||
let ctx =
|
let ctx =
|
||||||
Context(
|
Context(
|
||||||
db: pgo.Config(
|
db: pgo.Config(
|
||||||
|
@ -36,6 +36,7 @@ fn with_context(testcase: fn(Context) -> t) -> t {
|
||||||
)
|
)
|
||||||
|> pgo.connect,
|
|> pgo.connect,
|
||||||
config: test_config,
|
config: test_config,
|
||||||
|
cuid: cuid,
|
||||||
)
|
)
|
||||||
|
|
||||||
case link.prepare_database(ctx) {
|
case link.prepare_database(ctx) {
|
||||||
|
@ -52,39 +53,42 @@ fn with_context(testcase: fn(Context) -> t) -> t {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn null_test() {
|
fn cleanup() {
|
||||||
use ctx <- with_context()
|
use ctx <- with_context()
|
||||||
|
let assert Ok(_) =
|
||||||
pgo.execute(
|
pgo.execute("DROP TABLE link;", ctx.db, [], dynamic.dynamic)
|
||||||
"select $1",
|
|
||||||
ctx.db,
|
|
||||||
[pgo.null()],
|
|
||||||
dynamic.element(0, dynamic.optional(dynamic.int)),
|
|
||||||
)
|
|
||||||
|> should.equal(Ok(pgo.Returned(count: 1, rows: [None])))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_url_test() {
|
pub fn prepare_database_test() {
|
||||||
use ctx <- with_context()
|
use ctx <- with_context()
|
||||||
let test_id = Ok("testid_generate_url")
|
let res =
|
||||||
|
pgo.execute(
|
||||||
|
"SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'link';",
|
||||||
|
ctx.db,
|
||||||
|
[],
|
||||||
|
dynamic.tuple2(dynamic.string, dynamic.string),
|
||||||
|
)
|
||||||
|
|
||||||
link.generate_url(test_id, ctx)
|
res
|
||||||
|> should.equal(uri.parse("http://localhost:8080/l/testid_generate_url"))
|
|> should.be_ok
|
||||||
|
|> fn(x: Returned(#(String, String))) { x.rows }
|
||||||
|
|> should.equal([#("cuid", "character"), #("targeturl", "text")])
|
||||||
|
|
||||||
|
cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn store_link_test() {
|
pub fn store_retreive_test() {
|
||||||
use ctx <- with_context()
|
use ctx <- with_context()
|
||||||
let assert Ok(test_url) = uri.parse("http://example.com/")
|
let assert Ok(test_url) = uri.parse("http://example.com/")
|
||||||
let test_id = nanoid.generate()
|
let test_id = cuid.generate(ctx.cuid)
|
||||||
|
|
||||||
let stored_nano_id =
|
let cuid =
|
||||||
Link(test_id, test_url)
|
Link(test_id, test_url)
|
||||||
|> link.store(ctx)
|
|> link.store(ctx)
|
||||||
|> result.try_recover(fn(e) { e |> string.inspect |> Ok })
|
|> should.be_ok
|
||||||
|> result.unwrap("Undefined Error")
|
|
||||||
|
|
||||||
stored_nano_id |> should.equal(test_id)
|
cuid |> should.equal(test_id)
|
||||||
|
|
||||||
let assert Ok(stored_link) = stored_nano_id |> link.retrieve(ctx)
|
let assert Ok(stored_link) = cuid |> link.retrieve(ctx)
|
||||||
stored_link.target_url |> should.equal(test_url)
|
stored_link.target_url |> should.equal(test_url)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,26 @@ services:
|
||||||
db:
|
db:
|
||||||
image: postgres:latest
|
image: postgres:latest
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_USER=dumptruck
|
POSTGRES_USER: dumptruck
|
||||||
- POSTGRES_PASSWORD=dumptruck
|
POSTGRES_PASSWORD: dumptruck
|
||||||
- POSTGRES_DB=dumptruck
|
POSTGRES_DB: dumptruck
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
|
|
||||||
|
frontend:
|
||||||
|
image: dpage/pgadmin4
|
||||||
|
environment:
|
||||||
|
PGADMIN_DEFAULT_EMAIL: "test@example.com"
|
||||||
|
PGADMIN_DEFAULT_PASSWORD: "test"
|
||||||
|
PGADMIN_LISTEN_PORT: "80"
|
||||||
|
PGADMIN_CONFIG_SERVER_MODE: "False"
|
||||||
|
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"
|
||||||
|
depends_on:
|
||||||
|
- "db"
|
||||||
|
volumes:
|
||||||
|
- read_only: true
|
||||||
|
source: "servers.json"
|
||||||
|
target: "/pgadmin4/servers.json"
|
||||||
|
type: bind
|
||||||
|
ports:
|
||||||
|
- "8080:80"
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"Servers": {
|
||||||
|
"1": {
|
||||||
|
"Group": "Servers",
|
||||||
|
"Name": "Docker",
|
||||||
|
"Host": "db",
|
||||||
|
"Port": 5432,
|
||||||
|
"MaintenanceDB": "dumptruck",
|
||||||
|
"Username": "dumptruck",
|
||||||
|
"Password": "dumptruck",
|
||||||
|
"SSLMode": "prefer",
|
||||||
|
"Favorite": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue