Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions test/run.hml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import * as test_semver from "./test_semver.hml";
import * as test_manifest from "./test_manifest.hml";
import * as test_lockfile from "./test_lockfile.hml";
import * as test_cache from "./test_cache.hml";
import * as test_resolver from "./test_resolver.hml";
import * as test_github from "./test_github.hml";
import * as test_installer from "./test_installer.hml";

print("hpm Test Suite");
print("==============");
Expand All @@ -18,6 +21,9 @@ test_semver.run();
test_manifest.run();
test_lockfile.run();
test_cache.run();
test_resolver.run();
test_github.run();
test_installer.run();

// Print summary and exit with appropriate code
let exit_code = summary();
Expand Down
170 changes: 167 additions & 3 deletions test/test_cache.hml
Original file line number Diff line number Diff line change
@@ -1,9 +1,33 @@
// Tests for cache module

import { suite, test, assert, assert_eq, assert_true, assert_false, assert_not_null } from "./framework.hml";
import { getenv } from "@stdlib/env";
import { suite, test, assert, assert_eq, assert_true, assert_false, assert_null, assert_not_null } from "./framework.hml";
import { getenv, setenv } from "@stdlib/env";
import { exists, make_dir, remove_dir, write_file, remove_file, is_dir, list_dir } from "@stdlib/fs";
import * as cache from "../src/cache.hml";

// Helper to clean up test directories
fn cleanup_dir(path: string) {
if (!exists(path)) {
return;
}
if (!is_dir(path)) {
remove_file(path);
return;
}
let entries = list_dir(path);
let i = 0;
while (i < entries.length) {
let entry_path = path + "/" + entries[i];
if (is_dir(entry_path)) {
cleanup_dir(entry_path);
} else {
remove_file(entry_path);
}
i = i + 1;
}
remove_dir(path);
}

export fn run() {
suite("cache.get_cache_dir");

Expand All @@ -21,6 +45,16 @@ export fn run() {
}
});

test("cache dir ends with cache", fn() {
let dir = cache.get_cache_dir();
assert_true(dir.ends_with("/cache"), "should end with /cache");
});

test("cache dir is under .hpm", fn() {
let dir = cache.get_cache_dir();
assert_true(dir.contains("/.hpm/"), "should be under .hpm");
});

suite("cache.get_config_dir");

test("returns path", fn() {
Expand All @@ -29,6 +63,14 @@ export fn run() {
assert_true(dir.ends_with(".hpm"), "should end with .hpm");
});

test("config dir respects HOME", fn() {
let home = getenv("HOME");
if (home != null) {
let dir = cache.get_config_dir();
assert_true(dir.starts_with(home), "should be under HOME");
}
});

suite("cache.get_cached_tarball_path");

test("builds correct path", fn() {
Expand All @@ -45,12 +87,58 @@ export fn run() {
assert_true(path.contains("1.0.0"), "version without v");
});

test("handles complex version", fn() {
let path = cache.get_cached_tarball_path("owner", "repo", "v2.1.3-beta.1");
assert_true(path.contains("2.1.3-beta.1"), "complex version");
});

test("handles owner with hyphen", fn() {
let path = cache.get_cached_tarball_path("my-org", "repo", "1.0.0");
assert_true(path.contains("my-org"), "hyphenated owner");
});

test("handles repo with hyphen", fn() {
let path = cache.get_cached_tarball_path("owner", "my-repo", "1.0.0");
assert_true(path.contains("my-repo"), "hyphenated repo");
});

test("path structure is owner/repo/version.tar.gz", fn() {
let path = cache.get_cached_tarball_path("testowner", "testrepo", "1.2.3");
assert_true(path.contains("/testowner/testrepo/1.2.3.tar.gz"), "correct structure");
});

suite("cache.get_cached_path (alias)");

test("returns same as get_cached_tarball_path", fn() {
let path1 = cache.get_cached_tarball_path("owner", "repo", "1.0.0");
let path2 = cache.get_cached_path("owner", "repo", "1.0.0");
assert_eq(path1, path2, "paths should match");
});

suite("cache.is_cached");

test("returns false for non-cached package", fn() {
let cached = cache.is_cached("nonexistent", "fakerepo", "9.9.9");
assert_false(cached, "should not be cached");
});

test("returns false for non-cached version", fn() {
// A package that doesn't exist
let cached = cache.is_cached("testowner", "testrepo", "0.0.0-nonexistent");
assert_false(cached, "non-existent version not cached");
});

suite("cache.list_cached");

test("returns array", fn() {
let packages = cache.list_cached();
assert_not_null(packages, "should return array");
// Note: actual content depends on cache state
});

test("returns array with correct structure", fn() {
let packages = cache.list_cached();
// Even if empty, should be an array
assert_true(packages.length >= 0, "has length property");
});

suite("cache.get_cache_size");
Expand All @@ -59,4 +147,80 @@ export fn run() {
let size = cache.get_cache_size();
assert_true(size >= 0, "size >= 0");
});

test("returns i64 type for large caches", fn() {
let size = cache.get_cache_size();
// Just verify we can compare it
assert_true(size >= 0, "can compare size");
});

suite("cache.read_config");

test("returns object", fn() {
let config = cache.read_config();
assert_not_null(config, "should return config object");
});

test("returns empty object if no config", fn() {
// Default behavior when no config exists
let config = cache.read_config();
// Should at least return an object (could be empty)
assert_not_null(config, "config not null");
});

suite("cache.ensure_cache_dir");

test("returns cache directory path", fn() {
let dir = cache.ensure_cache_dir();
assert_not_null(dir, "should return path");
assert_true(dir.length > 0, "path not empty");
});

test("creates directory if not exists", fn() {
let dir = cache.ensure_cache_dir();
assert_true(exists(dir), "directory should exist");
});

suite("cache path edge cases");

test("handles empty version after v strip", fn() {
// Edge case: what if version is just "v"?
let path = cache.get_cached_tarball_path("owner", "repo", "v");
assert_true(path.ends_with(".tar.gz"), "still ends with .tar.gz");
});

test("handles version with multiple dots", fn() {
let path = cache.get_cached_tarball_path("owner", "repo", "1.2.3.4.5");
assert_true(path.contains("1.2.3.4.5"), "multi-dot version");
});

test("handles version with plus sign", fn() {
let path = cache.get_cached_tarball_path("owner", "repo", "1.0.0+build.123");
assert_true(path.contains("1.0.0+build.123"), "version with build metadata");
});

suite("cache operations");

test("store and retrieve tarball file", fn() {
let test_cache = "/tmp/hpm_test_cache";
cleanup_dir(test_cache);

// Create a test tarball
let test_tarball = "/tmp/hpm_test_tarball.tar.gz";
write_file(test_tarball, "fake tarball content for testing");

// Store in cache (this creates the directory structure)
make_dir(test_cache);
make_dir(test_cache + "/testowner");
make_dir(test_cache + "/testowner/testrepo");
let dest_path = test_cache + "/testowner/testrepo/1.0.0.tar.gz";
write_file(dest_path, "fake tarball content for testing");

// Verify the file exists
assert_true(exists(dest_path), "tarball stored");

// Clean up
cleanup_dir(test_cache);
remove_file(test_tarball);
});
}
108 changes: 108 additions & 0 deletions test/test_github.hml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Tests for github module (non-network functions)

import { suite, test, assert, assert_eq, assert_true, assert_false, assert_null, assert_not_null } from "./framework.hml";
import * as github from "../src/github.hml";

export fn run() {
suite("github.get_tarball_url");

test("builds correct URL with v prefix", fn() {
let url = github.get_tarball_url("hemlang", "sprout", "v1.0.0");
assert_eq(url, "https://github.com/hemlang/sprout/archive/refs/tags/v1.0.0.tar.gz", "url with v");
});

test("adds v prefix if missing", fn() {
let url = github.get_tarball_url("hemlang", "sprout", "1.0.0");
assert_eq(url, "https://github.com/hemlang/sprout/archive/refs/tags/v1.0.0.tar.gz", "url without v");
});

test("handles complex version strings", fn() {
let url = github.get_tarball_url("owner", "repo", "v2.1.3-beta.1");
assert_eq(url, "https://github.com/owner/repo/archive/refs/tags/v2.1.3-beta.1.tar.gz", "complex version");
});

test("handles different owners and repos", fn() {
let url = github.get_tarball_url("my-org", "my-package", "v0.0.1");
assert_eq(url, "https://github.com/my-org/my-package/archive/refs/tags/v0.0.1.tar.gz", "custom org/repo");
});

test("handles single digit version", fn() {
let url = github.get_tarball_url("test", "pkg", "v1");
assert_eq(url, "https://github.com/test/pkg/archive/refs/tags/v1.tar.gz", "single digit");
});

suite("github.get_token (environment/config)");

test("returns null when no token configured", fn() {
// Note: This test assumes no GITHUB_TOKEN is set in the test environment
// and no config file exists. It tests the fallback behavior.
// In a real environment, this might return an actual token.
let token = github.get_token();
// We can't assert null because there might be a token configured
// Just verify it doesn't crash
assert_true(token == null || token.length >= 0, "token is null or string");
});

suite("github base64 decoding (internal)");

// Test base64 decoding by encoding and checking known values
// The base64_decode function is internal but we can test it indirectly
// by testing get_package_json with mock responses

test("tarball URL is HTTPS", fn() {
let url = github.get_tarball_url("any", "repo", "v1.0.0");
assert_true(url.starts_with("https://"), "should use HTTPS");
});

test("tarball URL contains .tar.gz extension", fn() {
let url = github.get_tarball_url("owner", "repo", "v1.0.0");
assert_true(url.ends_with(".tar.gz"), "should end with .tar.gz");
});

test("tarball URL contains owner", fn() {
let url = github.get_tarball_url("myowner", "myrepo", "v1.0.0");
assert_true(url.contains("/myowner/"), "should contain owner");
});

test("tarball URL contains repo", fn() {
let url = github.get_tarball_url("myowner", "myrepo", "v1.0.0");
assert_true(url.contains("/myrepo/"), "should contain repo");
});

test("tarball URL uses archive path", fn() {
let url = github.get_tarball_url("owner", "repo", "v1.0.0");
assert_true(url.contains("/archive/refs/tags/"), "should use archive path");
});

suite("github URL edge cases");

test("handles owner with hyphen", fn() {
let url = github.get_tarball_url("my-org-name", "repo", "v1.0.0");
assert_true(url.contains("/my-org-name/"), "hyphenated owner");
});

test("handles repo with hyphen", fn() {
let url = github.get_tarball_url("owner", "my-cool-repo", "v1.0.0");
assert_true(url.contains("/my-cool-repo/"), "hyphenated repo");
});

test("handles owner with underscore", fn() {
let url = github.get_tarball_url("my_org", "repo", "v1.0.0");
assert_true(url.contains("/my_org/"), "underscored owner");
});

test("handles repo with numbers", fn() {
let url = github.get_tarball_url("owner", "repo123", "v1.0.0");
assert_true(url.contains("/repo123/"), "repo with numbers");
});

test("handles prerelease version", fn() {
let url = github.get_tarball_url("owner", "repo", "v1.0.0-alpha.1");
assert_true(url.contains("v1.0.0-alpha.1"), "prerelease in URL");
});

test("handles build metadata in version", fn() {
let url = github.get_tarball_url("owner", "repo", "v1.0.0+build.123");
assert_true(url.contains("v1.0.0+build.123"), "build metadata in URL");
});
}
Loading
Loading