From e6fa83447309d5a68ace6efa6e6ac747b9e910ae Mon Sep 17 00:00:00 2001 From: Luca Bilke Date: Tue, 2 Jul 2024 23:18:02 +0200 Subject: [PATCH] WIP --- README.md | 11 ++++---- src/app/config.gleam | 30 +++++++-------------- src/app/web/file.gleam | 12 ++++----- src/app/web/link.gleam | 10 +++---- test/app/web/file_test.gleam | 43 ++++++++++++++++++++--------- test/app/web/link_test.gleam | 52 +++++++++++++++++++----------------- test/docker-compose.yml | 24 ++++++++++++++--- test/servers.json | 15 +++++++++++ testenv | 6 +++++ 9 files changed, 128 insertions(+), 75 deletions(-) create mode 100644 test/servers.json create mode 100644 testenv diff --git a/README.md b/README.md index 3e808e4..14a1f30 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ gleam shell # Run an Erlang shell ## TODO -- [X] Handle link GET -- [X] Handle link POST -- [] Handle file POST -- [] Handle file GET -- [] Web Frontend +- [x] Handle link GET +- [x] Handle link POST +- [ ] Handle file POST +- [ ] Handle file GET +- [ ] Web Frontend +- [ ] fix config loading from env diff --git a/src/app/config.gleam b/src/app/config.gleam index 8ed02a3..4284fa9 100644 --- a/src/app/config.gleam +++ b/src/app/config.gleam @@ -69,25 +69,15 @@ pub fn load_config_from_env() -> Config { postgres_pool_size: 15, ) Config( - port: "PORT" - |> get_int_or(defaults.port), - max_bytes: "MAX_BYTES" - |> get_int_or(defaults.max_bytes), - url_root: "URL_ROOT" - |> get_string_or(defaults.url_root), - secret_key_base: "SECRET_KEY_BASE" - |> get_string_or(defaults.secret_key_base), - postgres_host: "POSTGRES_HOST" - |> get_string_or(defaults.postgres_host), - 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), + port: get_int_or("PORT", defaults.port), + max_bytes: get_int_or("MAX_BYTES", defaults.max_bytes), + url_root: get_string_or("URL_ROOT", defaults.url_root), + secret_key_base: get_string_or("SECRET_KEY_BASE", defaults.secret_key_base), + postgres_host: get_string_or("PG_HOST", defaults.postgres_host), + postgres_db: get_string_or("PG_DB", defaults.postgres_db), + postgres_user: get_string_or("PG_USER", defaults.postgres_user), + postgres_password: get_string_or("PG_PASSWORD", defaults.postgres_password), + postgres_pool_size: get_int_or("PG_POOL_SIZE", defaults.postgres_pool_size), + postgres_port: get_int_or("PG_PORT", defaults.postgres_port), ) } diff --git a/src/app/web/file.gleam b/src/app/web/file.gleam index d194d23..15635cf 100644 --- a/src/app/web/file.gleam +++ b/src/app/web/file.gleam @@ -11,10 +11,10 @@ import ids/cuid import wisp.{type Request, type Response} const schema = " -CREATE TABLE IF NOT EXISTS \"File\" ( - Cuid CHAR(25) PRIMARY KEY, - Md5Hash CHAR(32) - FileName TEXT +CREATE TABLE IF NOT EXISTS \"file\" ( + cuid CHAR(25) PRIMARY KEY, + md5hash CHAR(32), + 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) { let sql = - "INSERT INTO \"File\" (Cuid, Md5Hash) VALUES ($1, $2) RETURNING Cuid;" + "INSERT INTO \"file\" (cuid, md5hash) VALUES ($1, $2) RETURNING cuid;" case pgo.execute( 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) { - let sql = "SELECT Cuid,Md5Hash,FileName FROM \"File\" WHERE Cuid = $1;" + let sql = "SELECT cuid,md5hash,filename FROM \"file\" WHERE cuid = $1;" case pgo.execute( sql, diff --git a/src/app/web/link.gleam b/src/app/web/link.gleam index f29ed54..bb09a03 100644 --- a/src/app/web/link.gleam +++ b/src/app/web/link.gleam @@ -10,9 +10,9 @@ import ids/cuid import wisp.{type Request, type Response} const schema = " -CREATE TABLE IF NOT EXISTS \"Link\" ( - Cuid CHAR(25) PRIMARY KEY, - TargetURL TEXT +CREATE TABLE IF NOT EXISTS \"link\" ( + cuid CHAR(25) PRIMARY KEY, + 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) { let sql = - "INSERT INTO \"Link\" (Cuid, TargetURL) VALUES ($1, $2) RETURNING Cuid;" + "INSERT INTO \"link\" (cuid, targeturl) VALUES ($1, $2) RETURNING cuid;" case pgo.execute( 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) { - let sql = "SELECT Cuid,TargetURL FROM \"Link\" WHERE Cuid = $1;" + let sql = "SELECT cuid,targeturl FROM \"link\" WHERE cuid = $1;" case pgo.execute( sql, diff --git a/test/app/web/file_test.gleam b/test/app/web/file_test.gleam index d7c3c0c..854f0e6 100644 --- a/test/app/web/file_test.gleam +++ b/test/app/web/file_test.gleam @@ -1,10 +1,11 @@ import app/config.{type Context, Config, Context} import app/web/file import gleam/dynamic -import gleam/option.{None, Some} -import gleam/pgo +import gleam/option.{Some} +import gleam/pgo.{type Returned} import gleam/string import gleeunit/should +import ids/cuid const test_config = Config( port: 8080, @@ -20,6 +21,7 @@ const test_config = Config( ) fn with_context(testcase: fn(Context) -> t) -> t { + let assert Ok(cuid) = cuid.start() let ctx = Context( db: pgo.Config( @@ -33,6 +35,7 @@ fn with_context(testcase: fn(Context) -> t) -> t { ) |> pgo.connect, config: test_config, + cuid: cuid, ) case file.prepare_database(ctx) { @@ -44,25 +47,41 @@ fn with_context(testcase: fn(Context) -> t) -> t { ret } Error(e) -> { - e |> string.inspect |> panic + panic as string.inspect(e) } } } -pub fn null_test() { +fn cleanup() { use ctx <- with_context() + let assert Ok(_) = + pgo.execute("DROP TABLE file;", ctx.db, [], dynamic.dynamic) +} - pgo.execute( - "select $1", - ctx.db, - [pgo.null()], - dynamic.element(0, dynamic.optional(dynamic.int)), - ) - |> should.equal(Ok(pgo.Returned(count: 1, rows: [None]))) +pub fn prepare_database_test() { + use ctx <- with_context() + let res = + pgo.execute( + "SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'file';", + ctx.db, + [], + 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() { "f7b478961451483b8c251c788750d5ef" |> file.hash_to_path - |> should.equal(["f7", "b4", "78961451483b8c251c788750d5ef"]) + |> should.equal("/f7/b4/78961451483b8c251c788750d5ef") } diff --git a/test/app/web/link_test.gleam b/test/app/web/link_test.gleam index f229ffd..c5fd249 100644 --- a/test/app/web/link_test.gleam +++ b/test/app/web/link_test.gleam @@ -1,13 +1,12 @@ import app/config.{type Context, Config, Context} import app/web/link.{Link} import gleam/dynamic -import gleam/option.{None, Some} -import gleam/pgo -import gleam/result +import gleam/option.{Some} +import gleam/pgo.{type Returned} import gleam/string import gleam/uri import gleeunit/should -import ids/nanoid +import ids/cuid const test_config = Config( port: 8080, @@ -23,6 +22,7 @@ const test_config = Config( ) fn with_context(testcase: fn(Context) -> t) -> t { + let assert Ok(cuid) = cuid.start() let ctx = Context( db: pgo.Config( @@ -36,6 +36,7 @@ fn with_context(testcase: fn(Context) -> t) -> t { ) |> pgo.connect, config: test_config, + cuid: cuid, ) 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() - - pgo.execute( - "select $1", - ctx.db, - [pgo.null()], - dynamic.element(0, dynamic.optional(dynamic.int)), - ) - |> should.equal(Ok(pgo.Returned(count: 1, rows: [None]))) + let assert Ok(_) = + pgo.execute("DROP TABLE link;", ctx.db, [], dynamic.dynamic) } -pub fn generate_url_test() { +pub fn prepare_database_test() { 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) - |> should.equal(uri.parse("http://localhost:8080/l/testid_generate_url")) + res + |> 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() 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.store(ctx) - |> result.try_recover(fn(e) { e |> string.inspect |> Ok }) - |> result.unwrap("Undefined Error") + |> should.be_ok - 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) } diff --git a/test/docker-compose.yml b/test/docker-compose.yml index 55b0b0d..97392f7 100644 --- a/test/docker-compose.yml +++ b/test/docker-compose.yml @@ -2,8 +2,26 @@ services: db: image: postgres:latest environment: - - POSTGRES_USER=dumptruck - - POSTGRES_PASSWORD=dumptruck - - POSTGRES_DB=dumptruck + POSTGRES_USER: dumptruck + POSTGRES_PASSWORD: dumptruck + POSTGRES_DB: dumptruck ports: - "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" diff --git a/test/servers.json b/test/servers.json new file mode 100644 index 0000000..67d7253 --- /dev/null +++ b/test/servers.json @@ -0,0 +1,15 @@ +{ + "Servers": { + "1": { + "Group": "Servers", + "Name": "Docker", + "Host": "db", + "Port": 5432, + "MaintenanceDB": "dumptruck", + "Username": "dumptruck", + "Password": "dumptruck", + "SSLMode": "prefer", + "Favorite": true + } + } +} diff --git a/testenv b/testenv new file mode 100644 index 0000000..69e0e5e --- /dev/null +++ b/testenv @@ -0,0 +1,6 @@ +#!/bin/sh +export URL_ROOT="http://localhost:8080" +export PG_HOST="localhost" +export PG_DB="dumptruck" +export PG_USER="dumptruck" +export PG_PASSWORD="dumptruck"