This commit is contained in:
parent
716ac1a698
commit
833ff9f0bd
|
@ -20,7 +20,7 @@ jobs:
|
||||||
git config --unset "http.${GITHUB_SERVER_URL}/.extraHeader"
|
git config --unset "http.${GITHUB_SERVER_URL}/.extraHeader"
|
||||||
git lfs install --local
|
git lfs install --local
|
||||||
git lfs pull
|
git lfs pull
|
||||||
- name: Build
|
- name: Build and test
|
||||||
run: nix build -L .#li7y .#li7y-oci
|
run: nix build -L .#li7y .#li7y-oci
|
||||||
- name: Push OCI image
|
- name: Push OCI image
|
||||||
if: github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/master'
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,5 +3,6 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
/target
|
/target
|
||||||
|
/.tarpaulin_target
|
||||||
result*
|
result*
|
||||||
.pre-commit-config.yaml
|
.pre-commit-config.yaml
|
||||||
|
|
16
.tarpaulin.toml
Normal file
16
.tarpaulin.toml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# SPDX-FileCopyrightText: 2024 Simon Bruder <simon@sbruder.de>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
[default]
|
||||||
|
# Tarpaulin uses custom options
|
||||||
|
# that are incompatible with the default options.
|
||||||
|
# This sets a different target directory,
|
||||||
|
# so the files from tarpaulin do not interfere with the regular outputs.
|
||||||
|
target-dir = ".tarpaulin_target"
|
||||||
|
# Do not recompile everything on every run
|
||||||
|
skip-clean = true
|
||||||
|
|
||||||
|
[report]
|
||||||
|
out = ["Html"]
|
||||||
|
output-dir = "target/tarpaulin"
|
47
flake.nix
47
flake.nix
|
@ -74,12 +74,54 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
packages = rec {
|
packages =
|
||||||
li7y = naersk'.buildPackage {
|
let
|
||||||
|
# naersk does not easily allow overrideAttrs
|
||||||
|
commonNaerskConfigurarion = {
|
||||||
src = self;
|
src = self;
|
||||||
|
|
||||||
|
checkInputs = with pkgs; [
|
||||||
|
postgresql
|
||||||
|
postgresqlTestHook
|
||||||
|
];
|
||||||
|
|
||||||
|
doCheck = true;
|
||||||
|
|
||||||
|
# tests need to be able to create and drop databases
|
||||||
|
postgresqlTestUserOptions = "LOGIN SUPERUSER";
|
||||||
|
|
||||||
|
postgresqlTestSetupPost = ''
|
||||||
|
export DATABASE_URL="postgres://''${PGUSER}/''${PGDATABASE}?port=5432&host=''${PGHOST}"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Otherwise SQLx tries to infer the databse schema from an empty database
|
||||||
|
# (as it can only run the migrations once the test binary is built).
|
||||||
|
# Also, this enforces that the full query cache is included in the repository.
|
||||||
|
SQLX_OFFLINE = true;
|
||||||
};
|
};
|
||||||
|
in
|
||||||
|
rec {
|
||||||
|
li7y = naersk'.buildPackage commonNaerskConfigurarion;
|
||||||
default = li7y;
|
default = li7y;
|
||||||
|
|
||||||
|
li7y-tarpaulin = naersk'.buildPackage (commonNaerskConfigurarion // {
|
||||||
|
name = "li7y-tarpaulin";
|
||||||
|
|
||||||
|
checkInputs = commonNaerskConfigurarion.checkInputs ++ (with pkgs; [
|
||||||
|
cargo-tarpaulin
|
||||||
|
]);
|
||||||
|
|
||||||
|
dontBuild = true;
|
||||||
|
singleStep = true; # tarpaulin uses different options anyway
|
||||||
|
|
||||||
|
cargoTestCommands = _: [ "cargo tarpaulin" ];
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
rm -r $out
|
||||||
|
cp -r target/tarpaulin $out
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
|
||||||
li7y-oci = pkgs.dockerTools.buildLayeredImage {
|
li7y-oci = pkgs.dockerTools.buildLayeredImage {
|
||||||
name = "li7y";
|
name = "li7y";
|
||||||
tag = "latest";
|
tag = "latest";
|
||||||
|
@ -99,6 +141,7 @@
|
||||||
rustPackageDev
|
rustPackageDev
|
||||||
] ++ (with pkgs; [
|
] ++ (with pkgs; [
|
||||||
cargo-deny
|
cargo-deny
|
||||||
|
cargo-tarpaulin
|
||||||
cargo-watch
|
cargo-watch
|
||||||
clippy
|
clippy
|
||||||
graphviz
|
graphviz
|
||||||
|
|
|
@ -65,3 +65,132 @@ impl ItemRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use uuid::{uuid, Uuid};
|
||||||
|
|
||||||
|
use crate::database::{items::ItemAddForm, ItemRepository};
|
||||||
|
use crate::test_utils::*;
|
||||||
|
|
||||||
|
#[sqlx::test(fixtures(path = "../../../tests/fixtures", scripts("default")))]
|
||||||
|
async fn simple_success(pool: sqlx::PgPool) -> sqlx::Result<()> {
|
||||||
|
let repo = ItemRepository::new(pool.clone());
|
||||||
|
|
||||||
|
let added_item = repo
|
||||||
|
.add(ItemAddForm {
|
||||||
|
quantity: 1,
|
||||||
|
name: None,
|
||||||
|
parent: None,
|
||||||
|
class: uuid!("e993e21c-8558-49e7-a993-2a6a61c1d55c"),
|
||||||
|
original_packaging: None,
|
||||||
|
description: "descr".to_string(),
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
assert_eq!(added_item.len(), 1);
|
||||||
|
|
||||||
|
let added_item = added_item.first();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
sqlx::query_scalar(
|
||||||
|
"SELECT
|
||||||
|
items.name IS NULL
|
||||||
|
AND items.parent IS NULL
|
||||||
|
AND items.class = 'e993e21c-8558-49e7-a993-2a6a61c1d55c'
|
||||||
|
AND items.original_packaging IS NULL
|
||||||
|
AND items.description = 'descr'
|
||||||
|
FROM items
|
||||||
|
WHERE items.id = $1"
|
||||||
|
)
|
||||||
|
.bind(added_item)
|
||||||
|
.fetch_one(&pool)
|
||||||
|
.await?
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[sqlx::test(fixtures(path = "../../../tests/fixtures", scripts("default")))]
|
||||||
|
async fn complex_success(pool: sqlx::PgPool) -> sqlx::Result<()> {
|
||||||
|
let repo = ItemRepository::new(pool.clone());
|
||||||
|
|
||||||
|
let added_items = repo
|
||||||
|
.add(ItemAddForm {
|
||||||
|
quantity: 7,
|
||||||
|
name: Some("Yeeeet".to_string()),
|
||||||
|
parent: Some(uuid!("4fc0f5f4-4dca-4c24-844d-1f464cb32afa")),
|
||||||
|
class: uuid!("e993e21c-8558-49e7-a993-2a6a61c1d55c"),
|
||||||
|
original_packaging: Some(uuid!("554b11ce-fecb-4020-981e-acabbf7b5913")),
|
||||||
|
description: "Lorem ipsum.".to_string(),
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
assert_eq!(added_items.len(), 7);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
sqlx::query_scalar(
|
||||||
|
"SELECT
|
||||||
|
items.name = 'Yeeeet'
|
||||||
|
AND items.parent = '4fc0f5f4-4dca-4c24-844d-1f464cb32afa'
|
||||||
|
AND items.class = 'e993e21c-8558-49e7-a993-2a6a61c1d55c'
|
||||||
|
AND items.original_packaging = '554b11ce-fecb-4020-981e-acabbf7b5913'
|
||||||
|
AND items.description = 'Lorem ipsum.'
|
||||||
|
FROM items
|
||||||
|
WHERE items.id = ANY ($1)"
|
||||||
|
)
|
||||||
|
.bind(added_items)
|
||||||
|
.fetch_one(&pool)
|
||||||
|
.await?
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[sqlx::test(fixtures(path = "../../../tests/fixtures", scripts("default")))]
|
||||||
|
async fn invalid_references(pool: sqlx::PgPool) -> sqlx::Result<()> {
|
||||||
|
let repo = ItemRepository::new(pool.clone());
|
||||||
|
|
||||||
|
let item_count_before = item_count(&pool).await?;
|
||||||
|
|
||||||
|
assert!(repo
|
||||||
|
.add(ItemAddForm {
|
||||||
|
quantity: 1,
|
||||||
|
name: None,
|
||||||
|
parent: Some(Uuid::new_v4()),
|
||||||
|
class: uuid!("e993e21c-8558-49e7-a993-2a6a61c1d55c"),
|
||||||
|
original_packaging: None,
|
||||||
|
description: "".to_string(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
assert!(repo
|
||||||
|
.add(ItemAddForm {
|
||||||
|
quantity: 1,
|
||||||
|
name: None,
|
||||||
|
parent: None,
|
||||||
|
class: Uuid::new_v4(),
|
||||||
|
original_packaging: None,
|
||||||
|
description: "".to_string(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
assert!(repo
|
||||||
|
.add(ItemAddForm {
|
||||||
|
quantity: 1,
|
||||||
|
name: None,
|
||||||
|
parent: None,
|
||||||
|
class: uuid!("e993e21c-8558-49e7-a993-2a6a61c1d55c"),
|
||||||
|
original_packaging: Some(Uuid::new_v4()),
|
||||||
|
description: "".to_string(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
assert_eq!(item_count(&pool).await?, item_count_before);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,3 +16,48 @@ impl ItemRepository {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use uuid::{uuid, Uuid};
|
||||||
|
|
||||||
|
use crate::database::ItemRepository;
|
||||||
|
use crate::test_utils::*;
|
||||||
|
|
||||||
|
#[sqlx::test(fixtures(path = "../../../tests/fixtures", scripts("default")))]
|
||||||
|
async fn success(pool: sqlx::PgPool) -> sqlx::Result<()> {
|
||||||
|
let item_count_before = item_count(&pool).await?;
|
||||||
|
|
||||||
|
let repo = ItemRepository::new(pool.clone());
|
||||||
|
repo.delete(uuid!("663f45e6-b11a-4197-8ce4-c784ac9ee617"))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
assert_eq!(item_count(&pool).await?, item_count_before - 1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[sqlx::test(fixtures(path = "../../../tests/fixtures", scripts("default")))]
|
||||||
|
async fn prevented_by_constraint(pool: sqlx::PgPool) -> sqlx::Result<()> {
|
||||||
|
let repo = ItemRepository::new(pool.clone());
|
||||||
|
|
||||||
|
assert!(repo
|
||||||
|
.delete(uuid!("3003e61f-0824-4625-9b72-eeb9f11a6a26"))
|
||||||
|
.await
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
repo.delete(uuid!("554b11ce-fecb-4020-981e-acabbf7b5913"))
|
||||||
|
.await?;
|
||||||
|
repo.delete(uuid!("3003e61f-0824-4625-9b72-eeb9f11a6a26"))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[sqlx::test]
|
||||||
|
async fn invalid_id(pool: sqlx::PgPool) {
|
||||||
|
let repo = ItemRepository::new(pool.clone());
|
||||||
|
|
||||||
|
assert!(repo.delete(Uuid::new_v4()).await.is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -59,3 +59,89 @@ impl ItemRepository {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use uuid::uuid;
|
||||||
|
|
||||||
|
use crate::database::{items::ItemEditForm, ItemRepository};
|
||||||
|
|
||||||
|
#[sqlx::test(fixtures(path = "../../../tests/fixtures", scripts("default")))]
|
||||||
|
async fn prefill_form(pool: sqlx::PgPool) -> sqlx::Result<()> {
|
||||||
|
let repo = ItemRepository::new(pool.clone());
|
||||||
|
|
||||||
|
let form = repo
|
||||||
|
.edit_form(uuid!("663f45e6-b11a-4197-8ce4-c784ac9ee617"))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
assert_eq!(form.name, Some("Item 2".to_string()));
|
||||||
|
assert_eq!(
|
||||||
|
form.parent,
|
||||||
|
Some(uuid!("4fc0f5f4-4dca-4c24-844d-1f464cb32afa"))
|
||||||
|
);
|
||||||
|
assert_eq!(form.class, uuid!("8a979306-b4c6-4ef8-900d-68f64abb2975"));
|
||||||
|
assert_eq!(
|
||||||
|
form.original_packaging,
|
||||||
|
Some(uuid!("049298e2-73db-42fb-957d-a741655648b1"))
|
||||||
|
);
|
||||||
|
assert_eq!(form.description, "Lorem ipsum 3".to_string());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[sqlx::test(fixtures(path = "../../../tests/fixtures", scripts("default")))]
|
||||||
|
async fn edit(pool: sqlx::PgPool) -> sqlx::Result<()> {
|
||||||
|
let repo = ItemRepository::new(pool.clone());
|
||||||
|
|
||||||
|
repo.edit(
|
||||||
|
uuid!("554b11ce-fecb-4020-981e-acabbf7b5913"),
|
||||||
|
&ItemEditForm {
|
||||||
|
name: Some("Totally new name".to_string()),
|
||||||
|
parent: Some(uuid!("4fc0f5f4-4dca-4c24-844d-1f464cb32afa")),
|
||||||
|
class: uuid!("04527cc8-2fbf-4a99-aa0a-361252c8f6d3"),
|
||||||
|
original_packaging: Some(uuid!("3003e61f-0824-4625-9b72-eeb9f11a6a26")),
|
||||||
|
description: "never seen before".to_string(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
sqlx::query_scalar(
|
||||||
|
"SELECT
|
||||||
|
items.name = 'Totally new name'
|
||||||
|
AND items.parent = '4fc0f5f4-4dca-4c24-844d-1f464cb32afa'
|
||||||
|
AND items.class = '04527cc8-2fbf-4a99-aa0a-361252c8f6d3'
|
||||||
|
AND items.original_packaging = '3003e61f-0824-4625-9b72-eeb9f11a6a26'
|
||||||
|
AND items.description = 'never seen before'
|
||||||
|
FROM items
|
||||||
|
WHERE items.id = '554b11ce-fecb-4020-981e-acabbf7b5913'"
|
||||||
|
)
|
||||||
|
.fetch_one(&pool)
|
||||||
|
.await?
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[sqlx::test(fixtures(path = "../../../tests/fixtures", scripts("default")))]
|
||||||
|
async fn invalid_parameters(pool: sqlx::PgPool) -> sqlx::Result<()> {
|
||||||
|
let repo = ItemRepository::new(pool.clone());
|
||||||
|
|
||||||
|
// cycle
|
||||||
|
assert!(repo
|
||||||
|
.edit(
|
||||||
|
uuid!("4fc0f5f4-4dca-4c24-844d-1f464cb32afa"),
|
||||||
|
&ItemEditForm {
|
||||||
|
name: None,
|
||||||
|
parent: Some(uuid!("4fc0f5f4-4dca-4c24-844d-1f464cb32afa")),
|
||||||
|
class: uuid!("8a979306-b4c6-4ef8-900d-68f64abb2975"),
|
||||||
|
original_packaging: None,
|
||||||
|
description: "".to_string(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
use sqlx::query;
|
use sqlx::query;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::{ItemRepository, ItemName, ItemPreview};
|
use super::{ItemName, ItemPreview, ItemRepository};
|
||||||
use crate::database::item_states::ItemState;
|
use crate::database::item_states::ItemState;
|
||||||
|
|
||||||
pub struct ItemDetails {
|
pub struct ItemDetails {
|
||||||
|
|
|
@ -7,5 +7,7 @@ pub mod database;
|
||||||
pub mod frontend;
|
pub mod frontend;
|
||||||
pub mod label;
|
pub mod label;
|
||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_utils;
|
||||||
|
|
||||||
pub use config::Config;
|
pub use config::Config;
|
||||||
|
|
11
src/test_utils.rs
Normal file
11
src/test_utils.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// SPDX-FileCopyrightText: 2024 Simon Bruder <simon@sbruder.de>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
use sqlx::query_scalar;
|
||||||
|
|
||||||
|
pub async fn item_count(pool: &sqlx::PgPool) -> sqlx::Result<i64> {
|
||||||
|
query_scalar("SELECT count(id) FROM items")
|
||||||
|
.fetch_one(pool)
|
||||||
|
.await
|
||||||
|
}
|
33
tests/fixtures/default.sql
vendored
Normal file
33
tests/fixtures/default.sql
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
-- SPDX-FileCopyrightText: 2024 Simon Bruder <simon@sbruder.de>
|
||||||
|
--
|
||||||
|
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
|
INSERT INTO item_classes (id, name, parent, description) VALUES
|
||||||
|
('e993e21c-8558-49e7-a993-2a6a61c1d55c', 'Class 1', NULL, 'Lorem ipsum 1'),
|
||||||
|
('04527cc8-2fbf-4a99-aa0a-361252c8f6d3', 'Class 2', NULL, 'Lorem ipsum 2'),
|
||||||
|
('9d760792-ddb0-47a0-bed1-c27dc41b285b', 'Class 3', NULL, 'Lorem ipsum 3'),
|
||||||
|
('8a979306-b4c6-4ef8-900d-68f64abb2975', 'Subclass 1.1', 'e993e21c-8558-49e7-a993-2a6a61c1d55c', 'Lorem ipsum 4'),
|
||||||
|
('042fe283-f645-401c-9079-3bd3ab1c3dc9', 'Subclass 1.2', 'e993e21c-8558-49e7-a993-2a6a61c1d55c', 'Lorem ipsum 5'),
|
||||||
|
('01ad10ec-d3be-4346-9d44-4ebb7297a14d', 'Subclass 2.1', '04527cc8-2fbf-4a99-aa0a-361252c8f6d3', 'Lorem ipsum 6');
|
||||||
|
|
||||||
|
INSERT INTO items (id, name, parent, class, original_packaging, description) VALUES
|
||||||
|
('4fc0f5f4-4dca-4c24-844d-1f464cb32afa', 'Item 1', NULL, 'e993e21c-8558-49e7-a993-2a6a61c1d55c', NULL, 'Lorem ipsum 1'),
|
||||||
|
('049298e2-73db-42fb-957d-a741655648b1', 'Original Packaging of Item 2', NULL, '01ad10ec-d3be-4346-9d44-4ebb7297a14d', NULL, 'Lorem ipsum 2'),
|
||||||
|
('663f45e6-b11a-4197-8ce4-c784ac9ee617', 'Item 2', '4fc0f5f4-4dca-4c24-844d-1f464cb32afa', '8a979306-b4c6-4ef8-900d-68f64abb2975', '049298e2-73db-42fb-957d-a741655648b1', 'Lorem ipsum 3'),
|
||||||
|
('3003e61f-0824-4625-9b72-eeb9f11a6a26', 'Item 3', '4fc0f5f4-4dca-4c24-844d-1f464cb32afa', 'e993e21c-8558-49e7-a993-2a6a61c1d55c', NULL, 'Lorem ipsum 4'),
|
||||||
|
('554b11ce-fecb-4020-981e-acabbf7b5913', 'Item 4', '3003e61f-0824-4625-9b72-eeb9f11a6a26', 'e993e21c-8558-49e7-a993-2a6a61c1d55c', NULL, 'Lorem ipsum 5'),
|
||||||
|
('b9fce434-faa4-4242-bd06-9d3589fa41e7', 'Borrowed Item', NULL, '9d760792-ddb0-47a0-bed1-c27dc41b285b', NULL, 'Lorem ipsum 6'),
|
||||||
|
('2683d77f-2d9c-4a5c-b87f-6e1a99c69db0', 'Loaned Item', NULL, '9d760792-ddb0-47a0-bed1-c27dc41b285b', NULL, 'Lorem ipsum 7'),
|
||||||
|
('5ca9ed99-2e70-4723-9ae4-0bb5ab274366', 'Inactive Item', NULL, '9d760792-ddb0-47a0-bed1-c27dc41b285b', NULL, 'Lorem ipsum 8'),
|
||||||
|
('2da2643d-c759-48ab-8cdf-e4d46c8ecc69', 'Owned Item (bought)', NULL, '9d760792-ddb0-47a0-bed1-c27dc41b285b', NULL, 'Lorem ipsum 9');
|
||||||
|
|
||||||
|
DELETE FROM item_events WHERE item = ANY (ARRAY[
|
||||||
|
'b9fce434-faa4-4242-bd06-9d3589fa41e7',
|
||||||
|
'2da2643d-c759-48ab-8cdf-e4d46c8ecc69'
|
||||||
|
]::uuid[]);
|
||||||
|
|
||||||
|
INSERT INTO item_events (item, event, description) VALUES
|
||||||
|
('b9fce434-faa4-4242-bd06-9d3589fa41e7', 'borrow', 'from Jane Person'),
|
||||||
|
('2683d77f-2d9c-4a5c-b87f-6e1a99c69db0', 'loan', 'to Joe Person'),
|
||||||
|
('5ca9ed99-2e70-4723-9ae4-0bb5ab274366', 'gift', 'to Jude Person'),
|
||||||
|
('2da2643d-c759-48ab-8cdf-e4d46c8ecc69', 'buy', 'from garage sale');
|
Loading…
Reference in a new issue