diff --git a/.github/workflows/Test_DB.yml b/.github/workflows/Test_DB.yml new file mode 100644 index 0000000..5de5a72 --- /dev/null +++ b/.github/workflows/Test_DB.yml @@ -0,0 +1,47 @@ +name: PostgreSQLAdapter test +on: push + +jobs: + # Label of the container job + container-job: + # Containers must run in Linux based operating systems + runs-on: ubuntu-latest + # Docker Hub image that `container-job` executes in + container: node:10.18-jessie + + # Service containers to run with `container-job` + services: + # Label used to access the service container + postgres: + # Docker Hub image + image: postgres + # Provide the password for postgres + env: + POSTGRES_PASSWORD: postgres + # Set health checks to wait until postgres has started + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + # Maps tcp port 5432 on service container to the host + - 5432:5432 + + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@latest + with: + version: '1.7.1' + arch: 'x64' + - uses: julia-actions/julia-buildpkg@master + - name: Install SearchLight and SearchLightPostgreSQL + run: | + pwd + julia -e 'using Pkg; Pkg.add(url="https://github.com/GenieFramework/SearchLight.jl.git")' + julia -e 'using Pkg; Pkg.add(url="https://github.com/GenieFramework/SearchLightPostgreSQL.jl.git")' + julia -e 'import Pkg; Pkg.add("SafeTestsets")' + julia -e 'using Pkg; Pkg.activate(".")' + julia -e 'using Pkg; Pkg.resolve()' + shell: bash + - uses: julia-actions/julia-runtest@master diff --git a/.gitignore b/.gitignore index 2251642..6f8a1b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -Manifest.toml \ No newline at end of file +Manifest.toml +tests/db/migrations/* +**/.DS_Store* diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0c04c53 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cmake.configureOnOpen": false +} diff --git a/Project.toml b/Project.toml index f1196f3..885a098 100644 --- a/Project.toml +++ b/Project.toml @@ -5,12 +5,12 @@ version = "2.0.1" [deps] DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" LibPQ = "194296ae-ab2e-5f79-8cd4-7183a0a5a0d1" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" SearchLight = "340e8cb6-72eb-11e8-37ce-c97ebeb32050" [compat] -DataFrames = "1" LibPQ = "1" SearchLight = "2" julia = "1" diff --git a/src/SearchLightPostgreSQL.jl b/src/SearchLightPostgreSQL.jl index 0cdf3dd..ffede85 100644 --- a/src/SearchLightPostgreSQL.jl +++ b/src/SearchLightPostgreSQL.jl @@ -293,9 +293,14 @@ Runs a SQL DB query that creates the table `table_name` with the structure neede The table should contain one column, `version`, unique, as a string of maximum 30 chars long. """ function SearchLight.Migration.create_migrations_table(table_name::String = SearchLight.config.db_migrations_table_name) :: Nothing - SearchLight.query("CREATE TABLE $table_name (version varchar(30))") - @info "Created table $table_name" + queryString = "SELECT table_name FROM information_schema.tables WHERE table_name = '$table_name'" + if isempty(SearchLight.query(queryString)) + SearchLight.query("CREATE TABLE $table_name (version varchar(30))") + @info "Created table $table_name" + else + @info "Migration table exists." + end nothing end diff --git a/test/.DS_Store b/test/.DS_Store new file mode 100644 index 0000000..1ec903c Binary files /dev/null and b/test/.DS_Store differ diff --git a/test/Project.toml b/test/Project.toml new file mode 100644 index 0000000..bd6d221 --- /dev/null +++ b/test/Project.toml @@ -0,0 +1,7 @@ +[deps] +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" +SearchLight = "340e8cb6-72eb-11e8-37ce-c97ebeb32050" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +TestSetExtensions = "98d24dd4-01ad-11ea-1b02-c9a08f80db04" diff --git a/test/db/.DS_Store b/test/db/.DS_Store new file mode 100644 index 0000000..b1ea27f Binary files /dev/null and b/test/db/.DS_Store differ diff --git a/test/db/migrations/2019052410085235_create_table_users.jl b/test/db/migrations/2019052410085235_create_table_users.jl new file mode 100644 index 0000000..1ff4c4d --- /dev/null +++ b/test/db/migrations/2019052410085235_create_table_users.jl @@ -0,0 +1,23 @@ +module CreateTableUsers + +import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table + +function up() + create_table(:users) do + [ + primary_key() + column(:username, :string, limit = 100) + column(:password, :string, limit = 100) + column(:name, :string, limit = 100) + column(:email, :string, limit = 100) + ] + end + + add_index(:users, :username) +end + +function down() + drop_table(:users) +end + +end \ No newline at end of file diff --git a/test/db/migrations/2021061519495560_create_table_roles.jl b/test/db/migrations/2021061519495560_create_table_roles.jl new file mode 100644 index 0000000..7d32db8 --- /dev/null +++ b/test/db/migrations/2021061519495560_create_table_roles.jl @@ -0,0 +1,20 @@ +module CreateTableRoles + +import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table + +function up() + create_table(:roles) do + [ + primary_key() + column(:name, :string, limit = 100) + ] + end + + add_index(:roles, :name) +end + +function down() + drop_table(:roles) +end + +end diff --git a/test/db/migrations/2021061519503270_create_table_abilities.jl b/test/db/migrations/2021061519503270_create_table_abilities.jl new file mode 100644 index 0000000..603127d --- /dev/null +++ b/test/db/migrations/2021061519503270_create_table_abilities.jl @@ -0,0 +1,20 @@ +module CreateTableAbilities + +import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table + +function up() + create_table(:abilities) do + [ + primary_key() + column(:name, :string, limit = 100) + ] + end + + add_index(:abilities, :name) +end + +function down() + drop_table(:abilities) +end + +end diff --git a/test/db/migrations/2021061519532446_create_table_roles_users.jl b/test/db/migrations/2021061519532446_create_table_roles_users.jl new file mode 100644 index 0000000..279f57e --- /dev/null +++ b/test/db/migrations/2021061519532446_create_table_roles_users.jl @@ -0,0 +1,22 @@ +module CreateTableRolesUsers + +import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table + +function up() + create_table(:rolesusers) do + [ + primary_key() + column(:roles_id, :int) + column(:users_id, :int) + ] + end + + add_index(:rolesusers, :roles_id) + add_index(:rolesusers, :users_id) +end + +function down() + drop_table(:rolesusers) +end + +end diff --git a/test/db/migrations/2021061519540214_create_table_abilities_roles.jl b/test/db/migrations/2021061519540214_create_table_abilities_roles.jl new file mode 100644 index 0000000..b87c6b4 --- /dev/null +++ b/test/db/migrations/2021061519540214_create_table_abilities_roles.jl @@ -0,0 +1,22 @@ +module CreateTableAbilitiesRoles + +import SearchLight.Migrations: create_table, column, primary_key, add_index, drop_table + +function up() + create_table(:abilitiesroles) do + [ + primary_key() + column(:abilities_id, :int) + column(:roles_id, :int) + ] + end + + add_index(:abilitiesroles, :abilities_id) + add_index(:abilitiesroles, :roles_id) +end + +function down() + drop_table(:abilitiesroles) +end + +end diff --git a/test/models.jl b/test/models.jl new file mode 100644 index 0000000..164ddc2 --- /dev/null +++ b/test/models.jl @@ -0,0 +1,159 @@ +module TestModels + + using SearchLight + + ######## Model from Genie-Searchligth-example-app extracted ############ + export Book, BookWithInterns + using SearchLight, Dates + + ######## Model from Genie-Searchligth-example-app extracted ############ + export Callback + export seed, fields_to_store + + mutable struct Book <: AbstractModel + + ### FIELDS + id::DbId + title::String + author::String + cover::String + + ### VALIDATION + # validator::ModelValidator + + ### CALLBACKS + # before_save::Function + # after_save::Function + # on_save::Function + # on_find::Function + # after_find::Function + + ### SCOPES + # scopes::Dict{Symbol,Vector{SearchLight.SQLWhereEntity}} + + ### constructor + Book(; + ### FIELDS + id = DbId(), + title = "", + author = "", + cover = "", + + ### VALIDATION + # validator = ModelValidator([ + # ValidationRule(:title, BooksValidator.not_empty) + # ]), + + ### CALLBACKS + # before_save = (m::Todo) -> begin + # @info "Before save" + # end, + # after_save = (m::Todo) -> begin + # @info "After save" + # end, + # on_save = (m::Todo, field::Symbol, value::Any) -> begin + # @info "On save" + # end, + # on_find = (m::Todo, field::Symbol, value::Any) -> begin + # @info "On find" + # end, + # after_find = (m::Todo) -> begin + # @info "After find" + # end, + + ### SCOPES + # scopes = Dict{Symbol,Vector{SearchLight.SQLWhereEntity}}() + + ) = new(id, title, author, cover ### FIELDS + # validator, ### VALIDATION + # before_save, after_save, on_save, on_find, after_find ### CALLBACKS + # scopes ### SCOPES + ) + end + + mutable struct BookWithInterns <: AbstractModel + ### FIELDS + id::DbId + title::String + author::String + cover::String + + ### VALIDATION + # validator::ModelValidator + + ### CALLBACKS + # before_save::Function + # after_save::Function + # on_save::Function + # on_find::Function + # after_find::Function + + ### SCOPES + # scopes::Dict{Symbol,Vector{SearchLight.SQLWhereEntity}} + + ### constructor + BookWithInterns(; + ### FIELDS + id = DbId(), + title = "", + author = "", + cover = "", + + ### VALIDATION + # validator = ModelValidator([ + # ValidationRule(:title, BooksValidator.not_empty) + # ]), + + ### CALLBACKS + # before_save = (m::Todo) -> begin + # @info "Before save" + # end, + # after_save = (m::Todo) -> begin + # @info "After save" + # end, + # on_save = (m::Todo, field::Symbol, value::Any) -> begin + # @info "On save" + # end, + # on_find = (m::Todo, field::Symbol, value::Any) -> begin + # @info "On find" + # end, + # after_find = (m::Todo) -> begin + # @info "After find" + # end, + + ### SCOPES + # scopes = Dict{Symbol,Vector{SearchLight.SQLWhereEntity}}() + + ) = new("bookwithinterns", "id", Symbol[], ### INTERNALS + id, title, author, cover ### FIELDS + # validator, ### VALIDATION + # before_save, after_save, on_save, on_find, after_find ### CALLBACKS + # scopes ### SCOPES + ) + end + + Base.@kwdef mutable struct Callback <: AbstractModel + id::DbId = DbId() + title::String = "" + indicator::Bool = true + created_at::String = string(Dates.now()) + # callbacks + before_save::Function = (m::Callback) -> begin + @info "Do something before saving" + end + after_save::Function = (m::Callback) -> begin + @info "Do something after saving" + end + end + + function seed() + BillGatesBooks = [ + ("The Best We Could Do", "Thi Bui"), + ("Evicted: Poverty and Profit in the American City", "Matthew Desmond"), + ("Believe Me: A Memoir of Love, Death, and Jazz Chickens", "Eddie Izzard"), + ("The Sympathizer!", "Viet Thanh Nguyen"), + ("Energy and Civilization, A History", "Vaclav Smil") + ] + end + +end ### End Module diff --git a/test/postgres_connection.yml b/test/postgres_connection.yml new file mode 100644 index 0000000..8a795f2 --- /dev/null +++ b/test/postgres_connection.yml @@ -0,0 +1,13 @@ +env: dev + +dev: + adapter: PostgreSQL + host: postgres + port: 5432 + database: postgres + username: postgres + password: postgres + + config: + log_queries: true + log_level: :debug diff --git a/test/runtests.jl b/test/runtests.jl new file mode 100644 index 0000000..cf2946c --- /dev/null +++ b/test/runtests.jl @@ -0,0 +1,16 @@ +using Pkg + +using Test, TestSetExtensions, SafeTestsets +using SearchLight +using SearchLightPostgreSQL +using Dates + +# @testset ExtendedTestSet "SearchLight PostgreSQL adapter tests" begin +# @includetests ARGS +# end + +# run a simple connect test for the first time + +@testset ExtendedTestSet "SearchLight tests" begin + @includetests ARGS +end diff --git a/test/tests_relationships.jl b/test/tests_relationships.jl new file mode 100644 index 0000000..284036a --- /dev/null +++ b/test/tests_relationships.jl @@ -0,0 +1,51 @@ +using SearchLight, SearchLight.Migrations, SearchLight.Relationships + +#To reach the db/migrations folder change the directory is necessary +cd(@__DIR__) + +connection_file = joinpath(@__DIR__,"postgres_connection.yml") +conn_info_postgres = SearchLight.Configuration.load(connection_file) +const conn = SearchLight.connect(conn_info_postgres) + +try + SearchLight.Migrations.status() +catch _ + SearchLight.Migrations.create_migrations_table() +end + +isempty(SearchLight.Migrations.downed_migrations()) || SearchLight.Migrations.all_up!!() + +Base.@kwdef mutable struct User <: AbstractModel + id::DbId = DbId() + username::String = "" + password::String = "" + name::String = "" + email::String = "" +end + +Base.@kwdef mutable struct Role <: AbstractModel + id::DbId = DbId() + name::String = "" +end + +Base.@kwdef mutable struct Ability <: AbstractModel + id::DbId = DbId() + name::String = "" +end + +u1 = findone_or_create(User, username = "a") |> save! +r1 = findone_or_create(Role, name = "abcd") |> save! + +for x in 'a':'d' + findone_or_create(Ability, name = "$x") |> save! +end + +Relationships.Relationship!(u1, r1) + +for a in all(Ability) + Relationships.Relationship!(r1, a) +end + +Relationships.related(u1, Role) +Relationships.related(findone(Role, id = 1), Ability) +Relationships.related(u1, Ability, through = [Role])