From f425320b5869f4f080fba796509677e56bc544b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 18:04:51 +0000 Subject: [PATCH 1/2] Initial plan From 0fcd06b65c99135362b379eef07280ace486bef5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 18:33:25 +0000 Subject: [PATCH 2/2] Add comprehensive tests for generateTile function Co-authored-by: marlonmarcello <1956448+marlonmarcello@users.noreply.github.com> --- .gitignore | 5 + deno.json | 11 +- deno.lock | 165 +++++++++++++----- tests/fixtures/test-input.png | Bin 0 -> 27683 bytes tests/generate_tile_test.ts | 308 ++++++++++++++++++++++++++++++++++ 5 files changed, 447 insertions(+), 42 deletions(-) create mode 100644 .gitignore create mode 100644 tests/fixtures/test-input.png create mode 100644 tests/generate_tile_test.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ff9bd1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Test output +tests/output/ + +# Node modules (Deno node_modules dir) +node_modules/ diff --git a/deno.json b/deno.json index b61140f..03abde1 100644 --- a/deno.json +++ b/deno.json @@ -1,12 +1,15 @@ { + "nodeModulesDir": "auto", "imports": { - "@std/cli": "jsr:@std/cli@^1.0.27", - "@std/fs": "jsr:@std/fs@^1.0.22", - "@std/path": "jsr:@std/path@^1.1.4", + "@std/assert": "https://deno.land/std@0.224.0/assert/mod.ts", + "@std/cli": "https://deno.land/std@0.224.0/cli/mod.ts", + "@std/fs": "https://deno.land/std@0.224.0/fs/mod.ts", + "@std/path": "https://deno.land/std@0.224.0/path/mod.ts", "sharp": "npm:sharp@^0.34.5" }, "tasks": { - "compile": "deno compile --allow-all main.ts" + "compile": "deno compile --allow-all main.ts", + "test": "deno test -A" }, "version": "0.0.1" } diff --git a/deno.lock b/deno.lock index 52e04a2..9f15739 100644 --- a/deno.lock +++ b/deno.lock @@ -1,45 +1,10 @@ { "version": "5", "specifiers": { - "jsr:@std/cli@*": "1.0.27", - "jsr:@std/cli@^1.0.27": "1.0.27", - "jsr:@std/fmt@^1.0.9": "1.0.9", - "jsr:@std/fs@*": "1.0.22", - "jsr:@std/fs@^1.0.22": "1.0.22", - "jsr:@std/internal@^1.0.12": "1.0.12", - "jsr:@std/path@^1.1.4": "1.1.4", "npm:@imagemagick/magick-wasm@0.0.31": "0.0.31", "npm:@types/node@*": "24.2.0", "npm:sharp@~0.34.5": "0.34.5" }, - "jsr": { - "@std/cli@1.0.27": { - "integrity": "eba97edd0891871a7410e835dd94b3c260c709cca5983df2689c25a71fbe04de", - "dependencies": [ - "jsr:@std/fmt", - "jsr:@std/internal" - ] - }, - "@std/fmt@1.0.9": { - "integrity": "2487343e8899fb2be5d0e3d35013e54477ada198854e52dd05ed0422eddcabe0" - }, - "@std/fs@1.0.22": { - "integrity": "de0f277a58a867147a8a01bc1b181d0dfa80bfddba8c9cf2bacd6747bcec9308", - "dependencies": [ - "jsr:@std/internal", - "jsr:@std/path" - ] - }, - "@std/internal@1.0.12": { - "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027" - }, - "@std/path@1.1.4": { - "integrity": "1d2d43f39efb1b42f0b1882a25486647cb851481862dc7313390b2bb044314b5", - "dependencies": [ - "jsr:@std/internal" - ] - } - }, "npm": { "@emnapi/runtime@1.8.1": { "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", @@ -261,6 +226,133 @@ } }, "remote": { + "https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975", + "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834", + "https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293", + "https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7", + "https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74", + "https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd", + "https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff", + "https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46", + "https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b", + "https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c", + "https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491", + "https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68", + "https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3", + "https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7", + "https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29", + "https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a", + "https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a", + "https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8", + "https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693", + "https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31", + "https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5", + "https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8", + "https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb", + "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917", + "https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47", + "https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68", + "https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3", + "https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73", + "https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19", + "https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5", + "https://deno.land/std@0.224.0/fs/_create_walk_entry.ts": "5d9d2aaec05bcf09a06748b1684224d33eba7a4de24cf4cf5599991ca6b5b412", + "https://deno.land/std@0.224.0/fs/_get_file_info_type.ts": "da7bec18a7661dba360a1db475b826b18977582ce6fc9b25f3d4ee0403fe8cbd", + "https://deno.land/std@0.224.0/fs/_is_same_path.ts": "709c95868345fea051c58b9e96af95cff94e6ae98dfcff2b66dee0c212c4221f", + "https://deno.land/std@0.224.0/fs/_is_subdir.ts": "c68b309d46cc8568ed83c000f608a61bbdba0943b7524e7a30f9e450cf67eecd", + "https://deno.land/std@0.224.0/fs/_to_path_string.ts": "29bfc9c6c112254961d75cbf6ba814d6de5349767818eb93090cecfa9665591e", + "https://deno.land/std@0.224.0/fs/copy.ts": "7ab12a16adb65d155d4943c88081ca16ce3b0b5acada64c1ce93800653678039", + "https://deno.land/std@0.224.0/fs/empty_dir.ts": "e400e96e1d2c8c558a5a1712063bd43939e00619c1d1cc29959babc6f1639418", + "https://deno.land/std@0.224.0/fs/ensure_dir.ts": "51a6279016c65d2985f8803c848e2888e206d1b510686a509fa7cc34ce59d29f", + "https://deno.land/std@0.224.0/fs/ensure_file.ts": "67608cf550529f3d4aa1f8b6b36bf817bdc40b14487bf8f60e61cbf68f507cf3", + "https://deno.land/std@0.224.0/fs/ensure_link.ts": "5c98503ebfa9cc05e2f2efaa30e91e60b4dd5b43ebbda82f435c0a5c6e3ffa01", + "https://deno.land/std@0.224.0/fs/ensure_symlink.ts": "cafe904cebacb9a761977d6dbf5e3af938be946a723bb394080b9a52714fafe4", + "https://deno.land/std@0.224.0/fs/eol.ts": "18c4ac009d0318504c285879eb7f47942643f13619e0ff070a0edc59353306bd", + "https://deno.land/std@0.224.0/fs/exists.ts": "3d38cb7dcbca3cf313be343a7b8af18a87bddb4b5ca1bd2314be12d06533b50f", + "https://deno.land/std@0.224.0/fs/expand_glob.ts": "2e428d90acc6676b2aa7b5c78ef48f30641b13f1fe658e7976c9064fb4b05309", + "https://deno.land/std@0.224.0/fs/mod.ts": "c25e6802cbf27f3050f60b26b00c2d8dba1cb7fcdafe34c66006a7473b7b34d4", + "https://deno.land/std@0.224.0/fs/move.ts": "ca205d848908d7f217353bc5c623627b1333490b8b5d3ef4cab600a700c9bd8f", + "https://deno.land/std@0.224.0/fs/walk.ts": "cddf87d2705c0163bff5d7767291f05b0f46ba10b8b28f227c3849cace08d303", + "https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6", + "https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2", + "https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e", + "https://deno.land/std@0.224.0/path/_common/assert_path.ts": "dbdd757a465b690b2cc72fc5fb7698c51507dec6bfafce4ca500c46b76ff7bd8", + "https://deno.land/std@0.224.0/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2", + "https://deno.land/std@0.224.0/path/_common/common.ts": "ef73c2860694775fe8ffcbcdd387f9f97c7a656febf0daa8c73b56f4d8a7bd4c", + "https://deno.land/std@0.224.0/path/_common/constants.ts": "dc5f8057159f4b48cd304eb3027e42f1148cf4df1fb4240774d3492b5d12ac0c", + "https://deno.land/std@0.224.0/path/_common/dirname.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8", + "https://deno.land/std@0.224.0/path/_common/format.ts": "92500e91ea5de21c97f5fe91e178bae62af524b72d5fcd246d6d60ae4bcada8b", + "https://deno.land/std@0.224.0/path/_common/from_file_url.ts": "d672bdeebc11bf80e99bf266f886c70963107bdd31134c4e249eef51133ceccf", + "https://deno.land/std@0.224.0/path/_common/glob_to_reg_exp.ts": "6cac16d5c2dc23af7d66348a7ce430e5de4e70b0eede074bdbcf4903f4374d8d", + "https://deno.land/std@0.224.0/path/_common/normalize.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8", + "https://deno.land/std@0.224.0/path/_common/normalize_string.ts": "33edef773c2a8e242761f731adeb2bd6d683e9c69e4e3d0092985bede74f4ac3", + "https://deno.land/std@0.224.0/path/_common/relative.ts": "faa2753d9b32320ed4ada0733261e3357c186e5705678d9dd08b97527deae607", + "https://deno.land/std@0.224.0/path/_common/strip_trailing_separators.ts": "7024a93447efcdcfeaa9339a98fa63ef9d53de363f1fbe9858970f1bba02655a", + "https://deno.land/std@0.224.0/path/_common/to_file_url.ts": "7f76adbc83ece1bba173e6e98a27c647712cab773d3f8cbe0398b74afc817883", + "https://deno.land/std@0.224.0/path/_interface.ts": "8dfeb930ca4a772c458a8c7bbe1e33216fe91c253411338ad80c5b6fa93ddba0", + "https://deno.land/std@0.224.0/path/_os.ts": "8fb9b90fb6b753bd8c77cfd8a33c2ff6c5f5bc185f50de8ca4ac6a05710b2c15", + "https://deno.land/std@0.224.0/path/basename.ts": "7ee495c2d1ee516ffff48fb9a93267ba928b5a3486b550be73071bc14f8cc63e", + "https://deno.land/std@0.224.0/path/common.ts": "03e52e22882402c986fe97ca3b5bb4263c2aa811c515ce84584b23bac4cc2643", + "https://deno.land/std@0.224.0/path/constants.ts": "0c206169ca104938ede9da48ac952de288f23343304a1c3cb6ec7625e7325f36", + "https://deno.land/std@0.224.0/path/dirname.ts": "85bd955bf31d62c9aafdd7ff561c4b5fb587d11a9a5a45e2b01aedffa4238a7c", + "https://deno.land/std@0.224.0/path/extname.ts": "593303db8ae8c865cbd9ceec6e55d4b9ac5410c1e276bfd3131916591b954441", + "https://deno.land/std@0.224.0/path/format.ts": "6ce1779b0980296cf2bc20d66436b12792102b831fd281ab9eb08fa8a3e6f6ac", + "https://deno.land/std@0.224.0/path/from_file_url.ts": "911833ae4fd10a1c84f6271f36151ab785955849117dc48c6e43b929504ee069", + "https://deno.land/std@0.224.0/path/glob_to_regexp.ts": "7f30f0a21439cadfdae1be1bf370880b415e676097fda584a63ce319053b5972", + "https://deno.land/std@0.224.0/path/is_absolute.ts": "4791afc8bfd0c87f0526eaa616b0d16e7b3ab6a65b62942e50eac68de4ef67d7", + "https://deno.land/std@0.224.0/path/is_glob.ts": "a65f6195d3058c3050ab905705891b412ff942a292bcbaa1a807a74439a14141", + "https://deno.land/std@0.224.0/path/join.ts": "ae2ec5ca44c7e84a235fd532e4a0116bfb1f2368b394db1c4fb75e3c0f26a33a", + "https://deno.land/std@0.224.0/path/join_globs.ts": "5b3bf248b93247194f94fa6947b612ab9d3abd571ca8386cf7789038545e54a0", + "https://deno.land/std@0.224.0/path/mod.ts": "f6bd79cb08be0e604201bc9de41ac9248582699d1b2ee0ab6bc9190d472cf9cd", + "https://deno.land/std@0.224.0/path/normalize.ts": "4155743ccceeed319b350c1e62e931600272fad8ad00c417b91df093867a8352", + "https://deno.land/std@0.224.0/path/normalize_glob.ts": "cc89a77a7d3b1d01053b9dcd59462b75482b11e9068ae6c754b5cf5d794b374f", + "https://deno.land/std@0.224.0/path/parse.ts": "77ad91dcb235a66c6f504df83087ce2a5471e67d79c402014f6e847389108d5a", + "https://deno.land/std@0.224.0/path/posix/_util.ts": "1e3937da30f080bfc99fe45d7ed23c47dd8585c5e473b2d771380d3a6937cf9d", + "https://deno.land/std@0.224.0/path/posix/basename.ts": "d2fa5fbbb1c5a3ab8b9326458a8d4ceac77580961b3739cd5bfd1d3541a3e5f0", + "https://deno.land/std@0.224.0/path/posix/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4", + "https://deno.land/std@0.224.0/path/posix/constants.ts": "93481efb98cdffa4c719c22a0182b994e5a6aed3047e1962f6c2c75b7592bef1", + "https://deno.land/std@0.224.0/path/posix/dirname.ts": "76cd348ffe92345711409f88d4d8561d8645353ac215c8e9c80140069bf42f00", + "https://deno.land/std@0.224.0/path/posix/extname.ts": "e398c1d9d1908d3756a7ed94199fcd169e79466dd88feffd2f47ce0abf9d61d2", + "https://deno.land/std@0.224.0/path/posix/format.ts": "185e9ee2091a42dd39e2a3b8e4925370ee8407572cee1ae52838aed96310c5c1", + "https://deno.land/std@0.224.0/path/posix/from_file_url.ts": "951aee3a2c46fd0ed488899d024c6352b59154c70552e90885ed0c2ab699bc40", + "https://deno.land/std@0.224.0/path/posix/glob_to_regexp.ts": "76f012fcdb22c04b633f536c0b9644d100861bea36e9da56a94b9c589a742e8f", + "https://deno.land/std@0.224.0/path/posix/is_absolute.ts": "cebe561ad0ae294f0ce0365a1879dcfca8abd872821519b4fcc8d8967f888ede", + "https://deno.land/std@0.224.0/path/posix/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9", + "https://deno.land/std@0.224.0/path/posix/join.ts": "7fc2cb3716aa1b863e990baf30b101d768db479e70b7313b4866a088db016f63", + "https://deno.land/std@0.224.0/path/posix/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25", + "https://deno.land/std@0.224.0/path/posix/mod.ts": "2301fc1c54a28b349e20656f68a85f75befa0ee9b6cd75bfac3da5aca9c3f604", + "https://deno.land/std@0.224.0/path/posix/normalize.ts": "baeb49816a8299f90a0237d214cef46f00ba3e95c0d2ceb74205a6a584b58a91", + "https://deno.land/std@0.224.0/path/posix/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6", + "https://deno.land/std@0.224.0/path/posix/parse.ts": "09dfad0cae530f93627202f28c1befa78ea6e751f92f478ca2cc3b56be2cbb6a", + "https://deno.land/std@0.224.0/path/posix/relative.ts": "3907d6eda41f0ff723d336125a1ad4349112cd4d48f693859980314d5b9da31c", + "https://deno.land/std@0.224.0/path/posix/resolve.ts": "08b699cfeee10cb6857ccab38fa4b2ec703b0ea33e8e69964f29d02a2d5257cf", + "https://deno.land/std@0.224.0/path/posix/to_file_url.ts": "7aa752ba66a35049e0e4a4be5a0a31ac6b645257d2e031142abb1854de250aaf", + "https://deno.land/std@0.224.0/path/posix/to_namespaced_path.ts": "28b216b3c76f892a4dca9734ff1cc0045d135532bfd9c435ae4858bfa5a2ebf0", + "https://deno.land/std@0.224.0/path/relative.ts": "ab739d727180ed8727e34ed71d976912461d98e2b76de3d3de834c1066667add", + "https://deno.land/std@0.224.0/path/resolve.ts": "a6f977bdb4272e79d8d0ed4333e3d71367cc3926acf15ac271f1d059c8494d8d", + "https://deno.land/std@0.224.0/path/to_file_url.ts": "88f049b769bce411e2d2db5bd9e6fd9a185a5fbd6b9f5ad8f52bef517c4ece1b", + "https://deno.land/std@0.224.0/path/to_namespaced_path.ts": "b706a4103b104cfadc09600a5f838c2ba94dbcdb642344557122dda444526e40", + "https://deno.land/std@0.224.0/path/windows/_util.ts": "d5f47363e5293fced22c984550d5e70e98e266cc3f31769e1710511803d04808", + "https://deno.land/std@0.224.0/path/windows/basename.ts": "6bbc57bac9df2cec43288c8c5334919418d784243a00bc10de67d392ab36d660", + "https://deno.land/std@0.224.0/path/windows/common.ts": "26f60ccc8b2cac3e1613000c23ac5a7d392715d479e5be413473a37903a2b5d4", + "https://deno.land/std@0.224.0/path/windows/constants.ts": "5afaac0a1f67b68b0a380a4ef391bf59feb55856aa8c60dfc01bd3b6abb813f5", + "https://deno.land/std@0.224.0/path/windows/dirname.ts": "33e421be5a5558a1346a48e74c330b8e560be7424ed7684ea03c12c21b627bc9", + "https://deno.land/std@0.224.0/path/windows/extname.ts": "165a61b00d781257fda1e9606a48c78b06815385e7d703232548dbfc95346bef", + "https://deno.land/std@0.224.0/path/windows/format.ts": "bbb5ecf379305b472b1082cd2fdc010e44a0020030414974d6029be9ad52aeb6", + "https://deno.land/std@0.224.0/path/windows/from_file_url.ts": "ced2d587b6dff18f963f269d745c4a599cf82b0c4007356bd957cb4cb52efc01", + "https://deno.land/std@0.224.0/path/windows/glob_to_regexp.ts": "e45f1f89bf3fc36f94ab7b3b9d0026729829fabc486c77f414caebef3b7304f8", + "https://deno.land/std@0.224.0/path/windows/is_absolute.ts": "4a8f6853f8598cf91a835f41abed42112cebab09478b072e4beb00ec81f8ca8a", + "https://deno.land/std@0.224.0/path/windows/is_glob.ts": "8a8b08c08bf731acf2c1232218f1f45a11131bc01de81e5f803450a5914434b9", + "https://deno.land/std@0.224.0/path/windows/join.ts": "8d03530ab89195185103b7da9dfc6327af13eabdcd44c7c63e42e27808f50ecf", + "https://deno.land/std@0.224.0/path/windows/join_globs.ts": "a9475b44645feddceb484ee0498e456f4add112e181cb94042cdc6d47d1cdd25", + "https://deno.land/std@0.224.0/path/windows/mod.ts": "2301fc1c54a28b349e20656f68a85f75befa0ee9b6cd75bfac3da5aca9c3f604", + "https://deno.land/std@0.224.0/path/windows/normalize.ts": "78126170ab917f0ca355a9af9e65ad6bfa5be14d574c5fb09bb1920f52577780", + "https://deno.land/std@0.224.0/path/windows/normalize_glob.ts": "9c87a829b6c0f445d03b3ecadc14492e2864c3ebb966f4cea41e98326e4435c6", + "https://deno.land/std@0.224.0/path/windows/parse.ts": "08804327b0484d18ab4d6781742bf374976de662f8642e62a67e93346e759707", + "https://deno.land/std@0.224.0/path/windows/relative.ts": "3e1abc7977ee6cc0db2730d1f9cb38be87b0ce4806759d271a70e4997fc638d7", + "https://deno.land/std@0.224.0/path/windows/resolve.ts": "8dae1dadfed9d46ff46cc337c9525c0c7d959fb400a6308f34595c45bdca1972", + "https://deno.land/std@0.224.0/path/windows/to_file_url.ts": "40e560ee4854fe5a3d4d12976cef2f4e8914125c81b11f1108e127934ced502e", + "https://deno.land/std@0.224.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c", "https://deno.land/x/imagemagick_deno@0.0.14/mod.ts": "6b58c767d2308488597c3660e7ff399ede244198d7903900fa43a49cf93c7796", "https://deno.land/x/imagemagick_deno@0.0.14/src/alpha-option.ts": "749a9f3309e491ec09a1d6bc50ce95d9733887d9f57c6863c4ff1c7e9610227b", "https://deno.land/x/imagemagick_deno@0.0.14/src/auto-threshold-method.ts": "bb08a00046137e441930e56190b6db10c5fe657cb0a6142cd565a40b1c4250a2", @@ -317,9 +409,6 @@ }, "workspace": { "dependencies": [ - "jsr:@std/cli@^1.0.27", - "jsr:@std/fs@^1.0.22", - "jsr:@std/path@^1.1.4", "npm:sharp@~0.34.5" ] } diff --git a/tests/fixtures/test-input.png b/tests/fixtures/test-input.png new file mode 100644 index 0000000000000000000000000000000000000000..72a3de84b004e33f7eed1999824768d0b3cb0a03 GIT binary patch literal 27683 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 { + if (!existsSync(TEST_OUTPUT_DIR)) { + await Deno.mkdir(TEST_OUTPUT_DIR, { recursive: true }); + } + assertEquals(existsSync(TEST_OUTPUT_DIR), true); +}); + +Deno.test("generateTile - generates tile with 0° angle", async () => { + const metadata = await getImageProperties(TEST_INPUT); + const rationalAngle = findClosestRationalAngle(0); + const tileDims = calculateTileDimensions( + metadata.width, + metadata.height, + rationalAngle, + ); + + const output = `${TEST_OUTPUT_DIR}/test-0deg.png`; + + await generateTile({ + input: TEST_INPUT, + output, + metadata, + tileWidth: tileDims.width, + tileHeight: tileDims.height, + angle: rationalAngle.degrees, + limitInputPixels: false, + quality: 90, + tileMargin: 1, + verbose: false, + }); + + // Verify output file exists + assertEquals(existsSync(output), true); + + // Verify output has correct dimensions + const outputMetadata = await getImageProperties(output); + assertEquals(outputMetadata.width, tileDims.width); + assertEquals(outputMetadata.height, tileDims.height); + assertEquals(outputMetadata.format, "png"); +}); + +Deno.test("generateTile - generates tile with 45° angle", async () => { + const metadata = await getImageProperties(TEST_INPUT); + const rationalAngle = findClosestRationalAngle(45); + const tileDims = calculateTileDimensions( + metadata.width, + metadata.height, + rationalAngle, + ); + + const output = `${TEST_OUTPUT_DIR}/test-45deg.png`; + + await generateTile({ + input: TEST_INPUT, + output, + metadata, + tileWidth: tileDims.width, + tileHeight: tileDims.height, + angle: rationalAngle.degrees, + limitInputPixels: false, + quality: 90, + tileMargin: 1, + verbose: false, + }); + + // Verify output file exists + assertEquals(existsSync(output), true); + + // Verify output has correct dimensions + const outputMetadata = await getImageProperties(output); + assertEquals(outputMetadata.width, tileDims.width); + assertEquals(outputMetadata.height, tileDims.height); + assertEquals(outputMetadata.format, "png"); +}); + +Deno.test("generateTile - generates tile with 26.565° angle", async () => { + const metadata = await getImageProperties(TEST_INPUT); + const rationalAngle = findClosestRationalAngle(26.565); + const tileDims = calculateTileDimensions( + metadata.width, + metadata.height, + rationalAngle, + ); + + const output = `${TEST_OUTPUT_DIR}/test-26deg.png`; + + await generateTile({ + input: TEST_INPUT, + output, + metadata, + tileWidth: tileDims.width, + tileHeight: tileDims.height, + angle: rationalAngle.degrees, + limitInputPixels: false, + quality: 90, + tileMargin: 1, + verbose: false, + }); + + // Verify output file exists + assertEquals(existsSync(output), true); + + // Verify output has correct dimensions + const outputMetadata = await getImageProperties(output); + assertEquals(outputMetadata.width, tileDims.width); + assertEquals(outputMetadata.height, tileDims.height); + assertEquals(outputMetadata.format, "png"); +}); + +Deno.test("generateTile - respects quality parameter", async () => { + const metadata = await getImageProperties(TEST_INPUT); + const rationalAngle = findClosestRationalAngle(0); + const tileDims = calculateTileDimensions( + metadata.width, + metadata.height, + rationalAngle, + ); + + const outputHigh = `${TEST_OUTPUT_DIR}/test-quality-high.png`; + const outputLow = `${TEST_OUTPUT_DIR}/test-quality-low.png`; + + // Generate with high quality + await generateTile({ + input: TEST_INPUT, + output: outputHigh, + metadata, + tileWidth: tileDims.width, + tileHeight: tileDims.height, + angle: rationalAngle.degrees, + limitInputPixels: false, + quality: 100, + tileMargin: 1, + verbose: false, + }); + + // Generate with low quality + await generateTile({ + input: TEST_INPUT, + output: outputLow, + metadata, + tileWidth: tileDims.width, + tileHeight: tileDims.height, + angle: rationalAngle.degrees, + limitInputPixels: false, + quality: 10, + tileMargin: 1, + verbose: false, + }); + + // Verify both files exist + assertEquals(existsSync(outputHigh), true); + assertEquals(existsSync(outputLow), true); + + // High quality file should generally be larger than low quality + const statsHigh = await Deno.stat(outputHigh); + const statsLow = await Deno.stat(outputLow); + + // Both should be valid PNG files with correct dimensions + const metadataHigh = await getImageProperties(outputHigh); + const metadataLow = await getImageProperties(outputLow); + + assertEquals(metadataHigh.width, tileDims.width); + assertEquals(metadataHigh.height, tileDims.height); + assertEquals(metadataLow.width, tileDims.width); + assertEquals(metadataLow.height, tileDims.height); +}); + +Deno.test("generateTile - handles different tileMargin values", async () => { + const metadata = await getImageProperties(TEST_INPUT); + const rationalAngle = findClosestRationalAngle(45); + const tileDims = calculateTileDimensions( + metadata.width, + metadata.height, + rationalAngle, + ); + + const outputMargin1 = `${TEST_OUTPUT_DIR}/test-margin-1.png`; + const outputMargin3 = `${TEST_OUTPUT_DIR}/test-margin-3.png`; + + // Generate with margin 1 + await generateTile({ + input: TEST_INPUT, + output: outputMargin1, + metadata, + tileWidth: tileDims.width, + tileHeight: tileDims.height, + angle: rationalAngle.degrees, + limitInputPixels: false, + quality: 90, + tileMargin: 1, + verbose: false, + }); + + // Generate with margin 3 + await generateTile({ + input: TEST_INPUT, + output: outputMargin3, + metadata, + tileWidth: tileDims.width, + tileHeight: tileDims.height, + angle: rationalAngle.degrees, + limitInputPixels: false, + quality: 90, + tileMargin: 3, + verbose: false, + }); + + // Verify both files exist and have correct dimensions + assertEquals(existsSync(outputMargin1), true); + assertEquals(existsSync(outputMargin3), true); + + const metadataMargin1 = await getImageProperties(outputMargin1); + const metadataMargin3 = await getImageProperties(outputMargin3); + + assertEquals(metadataMargin1.width, tileDims.width); + assertEquals(metadataMargin1.height, tileDims.height); + assertEquals(metadataMargin3.width, tileDims.width); + assertEquals(metadataMargin3.height, tileDims.height); +}); + +Deno.test("generateTile - verbose flag doesn't break execution", async () => { + const metadata = await getImageProperties(TEST_INPUT); + const rationalAngle = findClosestRationalAngle(0); + const tileDims = calculateTileDimensions( + metadata.width, + metadata.height, + rationalAngle, + ); + + const output = `${TEST_OUTPUT_DIR}/test-verbose.png`; + + // This should log additional info but not break + await generateTile({ + input: TEST_INPUT, + output, + metadata, + tileWidth: tileDims.width, + tileHeight: tileDims.height, + angle: rationalAngle.degrees, + limitInputPixels: false, + quality: 90, + tileMargin: 1, + verbose: true, + }); + + assertEquals(existsSync(output), true); + + const outputMetadata = await getImageProperties(output); + assertEquals(outputMetadata.width, tileDims.width); + assertEquals(outputMetadata.height, tileDims.height); +}); + +Deno.test("generateTile - creates valid PNG output", async () => { + const metadata = await getImageProperties(TEST_INPUT); + const rationalAngle = findClosestRationalAngle(45); + const tileDims = calculateTileDimensions( + metadata.width, + metadata.height, + rationalAngle, + ); + + const output = `${TEST_OUTPUT_DIR}/test-valid-png.png`; + + await generateTile({ + input: TEST_INPUT, + output, + metadata, + tileWidth: tileDims.width, + tileHeight: tileDims.height, + angle: rationalAngle.degrees, + limitInputPixels: false, + quality: 90, + tileMargin: 1, + verbose: false, + }); + + // Verify it's a valid PNG by reading its properties + const outputMetadata = await getImageProperties(output); + assertExists(outputMetadata.width); + assertExists(outputMetadata.height); + assertEquals(outputMetadata.format, "png"); + + // Dimensions should match expected tile dimensions + assertEquals(outputMetadata.width, tileDims.width); + assertEquals(outputMetadata.height, tileDims.height); +}); + +// Cleanup +Deno.test("cleanup - remove output directory", async () => { + if (existsSync(TEST_OUTPUT_DIR)) { + await Deno.remove(TEST_OUTPUT_DIR, { recursive: true }); + } + assertEquals(existsSync(TEST_OUTPUT_DIR), false); +});