This commit is contained in:
34
src/docs/.gitignore
vendored
34
src/docs/.gitignore
vendored
@@ -1,34 +0,0 @@
|
||||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Production
|
||||
/build
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
bun.lockb
|
||||
bun.lock
|
||||
|
||||
yarn.lock
|
||||
|
||||
build.sh
|
||||
build_dev.sh
|
||||
develop.sh
|
||||
|
||||
docusaurus.config.ts
|
||||
|
||||
sidebars.ts
|
||||
|
||||
tsconfig.json
|
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"style": "dark",
|
||||
"links": [
|
||||
{
|
||||
"title": "Web",
|
||||
"items": [
|
||||
{
|
||||
"label": "ThreeFold.io",
|
||||
"href": "https://threefold.io"
|
||||
},
|
||||
{
|
||||
"href": "https://mycelium.threefold.io/",
|
||||
"label": "Mycelium Network"
|
||||
},
|
||||
{
|
||||
"href": "https://aibox.threefold.io/",
|
||||
"label": "AI Box"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"title": "ThreeFold HeroScript",
|
||||
"tagline": "ThreeFold HeroScript",
|
||||
"favicon": "img/favicon.png",
|
||||
"url": "https://threefold.info",
|
||||
"url_home": "docs/intro",
|
||||
"baseUrl": "/heroscript/",
|
||||
"image": "img/tf_graph.png",
|
||||
"metadata": {
|
||||
"description": "Internet Infrastructur for Everyone by Everyone, Everywhere.",
|
||||
"image": "https://threefold.info/tfgrid4/img/tf_graph.png",
|
||||
"title": "ThreeFold"
|
||||
},
|
||||
"buildDest":["root@info.ourworld.tf:/root/hero/www/info/heroscript"],
|
||||
"buildDestDev":["root@info.ourworld.tf:/root/hero/www/infodev/heroscript"],
|
||||
"copyright": "ThreeFold"
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"title": "",
|
||||
"logo": {
|
||||
"alt": "ThreeFold Logo",
|
||||
"src": "img/logo.svg",
|
||||
"srcDark": "img/new_logo_tft.png"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"href": "https://threefold.io",
|
||||
"label": "ThreeFold.io",
|
||||
"position": "right"
|
||||
},
|
||||
{
|
||||
"href": "https://mycelium.threefold.io/",
|
||||
"label": "Mycelium Network",
|
||||
"position": "right"
|
||||
},
|
||||
{
|
||||
"href": "https://aibox.threefold.io/",
|
||||
"label": "AI Box",
|
||||
"position": "right"
|
||||
}
|
||||
]
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
---
|
||||
title: "intro"
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# HeroScript
|
||||
|
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"label": "SAL",
|
||||
"position": 6,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "Tools to work with operating system."
|
||||
}
|
||||
}
|
@@ -1,239 +0,0 @@
|
||||
---
|
||||
title: "build containers"
|
||||
sidebar_position: 20
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Container Builder
|
||||
|
||||
The Buildah module provides functions for working with containers and images using the Buildah tool. Buildah helps you create and manage container images.
|
||||
|
||||
## Builder Pattern
|
||||
|
||||
The Buildah module now supports a Builder pattern, which provides a more intuitive and flexible way to work with containers and images.
|
||||
|
||||
### Creating a Builder
|
||||
|
||||
```js
|
||||
// Create a builder with a name and base image
|
||||
let builder = bah_new("my-container", "alpine:latest");
|
||||
|
||||
// Access builder properties
|
||||
let container_id = builder.container_id;
|
||||
let name = builder.name;
|
||||
let image = builder.image;
|
||||
```
|
||||
|
||||
### Builder Methods
|
||||
The Builder object provides the following methods:
|
||||
|
||||
- `run(command)`: Run a command in the container
|
||||
- `run_with_isolation(command, isolation)`: Run a command with specified isolation
|
||||
- `copy(source, dest)`: Copy files into the container
|
||||
- `add(source, dest)`: Add files into the container
|
||||
- `commit(image_name)`: Commit the container to an image
|
||||
- `remove()`: Remove the container
|
||||
- `reset()`: Remove the container and clear the container_id
|
||||
- `config(options)`: Configure container metadata
|
||||
- `set_entrypoint(entrypoint)`: Set the entrypoint for the container
|
||||
- `set_cmd(cmd)`: Set the default command for the container
|
||||
- `debug_mode`: Get or set the debug flag (true/false)
|
||||
- `write_content(content, dest_path)`: Write content to a file in the container
|
||||
- `read_content(source_path)`: Read content from a file in the container
|
||||
- `images()`: List images in local storage
|
||||
- `image_remove(image)`: Remove an image
|
||||
- `image_pull(image, tls_verify)`: Pull an image from a registry
|
||||
- `image_push(image, destination, tls_verify)`: Push an image to a registry
|
||||
- `image_tag(image, new_name)`: Add a tag to an image
|
||||
- `build(tag, context_dir, file, isolation)`: Build an image from a Dockerfile
|
||||
- `build(tag, context_dir, file, isolation)`: Build an image from a Dockerfile
|
||||
|
||||
### Example
|
||||
|
||||
```js
|
||||
// Create a builder
|
||||
let builder = bah_new("my-container", "alpine:latest");
|
||||
|
||||
// Enable debug mode to see command output
|
||||
builder.debug_mode = true;
|
||||
|
||||
// Reset the builder to remove any existing container
|
||||
builder.reset();
|
||||
|
||||
// Create a new container
|
||||
builder = bah_new("my-container", "alpine:latest");
|
||||
|
||||
// Run a command
|
||||
let result = builder.run("echo 'Hello from container'");
|
||||
println(`Command output: ${result.stdout}`);
|
||||
|
||||
// Write content directly to a file in the container
|
||||
let script_content = `#!/bin/sh
|
||||
echo "Hello from startup script"
|
||||
`;
|
||||
builder.write_content(script_content, "/start.sh");
|
||||
builder.run("chmod +x /start.sh");
|
||||
|
||||
// Set the entrypoint for the container
|
||||
builder.set_entrypoint("/start.sh");
|
||||
|
||||
// Add a file
|
||||
file_write("test_file.txt", "Test content");
|
||||
builder.add("test_file.txt", "/");
|
||||
|
||||
// Commit to an image
|
||||
builder.commit("my-custom-image:latest");
|
||||
|
||||
// Clean up
|
||||
builder.remove();
|
||||
delete("test_file.txt");
|
||||
```
|
||||
|
||||
## Image Information
|
||||
|
||||
### Image Properties
|
||||
|
||||
When working with images, you can access the following information:
|
||||
|
||||
- `id`: The unique identifier for the image
|
||||
- `names`: A list of names/tags for the image
|
||||
- `name`: The primary name of the image, or `<none>` if the image has no names
|
||||
- `size`: The size of the image
|
||||
- `created`: When the image was created
|
||||
|
||||
## Builder Methods
|
||||
|
||||
### `bah_new(name, image)`
|
||||
|
||||
Creates a new Builder object for working with a container.
|
||||
|
||||
**Parameters:**
|
||||
- `name` (string): The name to give the container
|
||||
- `image` (string): The name or ID of the image to create the container from
|
||||
|
||||
**Returns:** A Builder object if successful.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Create a new Builder
|
||||
let builder = bah_new("my-container", "alpine:latest");
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- If a container with the given name already exists, it will be reused instead of creating a new one
|
||||
- The Builder object provides methods for working with the container
|
||||
|
||||
### `reset()`
|
||||
|
||||
Resets a Builder by removing the container and clearing the container_id. This allows you to start fresh with the same Builder object.
|
||||
|
||||
**Returns:** Nothing.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Create a Builder
|
||||
let builder = bah_new("my-container", "alpine:latest");
|
||||
|
||||
// Reset the Builder to remove the container
|
||||
builder.reset();
|
||||
|
||||
// Create a new container with the same name
|
||||
builder = bah_new("my-container", "alpine:latest");
|
||||
```
|
||||
|
||||
### `debug_mode`
|
||||
|
||||
Get or set the debug flag for the Builder. When debug mode is enabled, all buildah commands will output their stdout/stderr, making it easier to debug issues.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Create a Builder
|
||||
let builder = bah_new("my-container", "alpine:latest");
|
||||
|
||||
// Enable debug mode
|
||||
builder.debug_mode = true;
|
||||
|
||||
// Run a command with debug output
|
||||
builder.run("echo 'Hello with debug'");
|
||||
|
||||
// Disable debug mode
|
||||
builder.debug_mode = false;
|
||||
```
|
||||
|
||||
### `set_entrypoint(entrypoint)`
|
||||
|
||||
Sets the entrypoint for the container. The entrypoint is the command that will be executed when the container starts.
|
||||
|
||||
**Parameters:**
|
||||
- `entrypoint` (string): The entrypoint command
|
||||
|
||||
**Returns:** Command result if successful.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Create a Builder
|
||||
let builder = bah_new("my-container", "alpine:latest");
|
||||
|
||||
// Set the entrypoint
|
||||
builder.set_entrypoint("/start.sh");
|
||||
```
|
||||
|
||||
### `set_cmd(cmd)`
|
||||
|
||||
Sets the default command for the container. This is used as arguments to the entrypoint.
|
||||
|
||||
**Parameters:**
|
||||
- `cmd` (string): The default command
|
||||
|
||||
**Returns:** Command result if successful.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Create a Builder
|
||||
let builder = bah_new("my-container", "alpine:latest");
|
||||
|
||||
// Set the default command
|
||||
builder.set_cmd("--verbose");
|
||||
```
|
||||
|
||||
### `write_content(content, dest_path)`
|
||||
|
||||
Writes content to a file in the container.
|
||||
|
||||
**Parameters:**
|
||||
- `content` (string): The content to write
|
||||
- `dest_path` (string): The destination path in the container
|
||||
|
||||
**Returns:** Command result if successful.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Create a Builder
|
||||
let builder = bah_new("my-container", "alpine:latest");
|
||||
|
||||
// Write content to a file
|
||||
let content = "Hello, world!";
|
||||
builder.write_content(content, "/hello.txt");
|
||||
```
|
||||
|
||||
### `read_content(source_path)`
|
||||
|
||||
Reads content from a file in the container.
|
||||
|
||||
**Parameters:**
|
||||
- `source_path` (string): The source path in the container
|
||||
|
||||
**Returns:** The file content as a string if successful.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Create a Builder
|
||||
let builder = bah_new("my-container", "alpine:latest");
|
||||
|
||||
// Write content to a file
|
||||
builder.write_content("Hello, world!", "/hello.txt");
|
||||
|
||||
// Read content from the file
|
||||
let content = builder.read_content("/hello.txt");
|
||||
println(content); // Outputs: Hello, world!
|
||||
```
|
@@ -1,210 +0,0 @@
|
||||
---
|
||||
title: "git"
|
||||
sidebar_position: 5
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Git
|
||||
|
||||
This module provides HeroScript wrappers for the Git functionality in SAL.
|
||||
|
||||
> **Note:** The constructor for GitTree has been renamed from `new()` to `gittree_new()` to avoid confusion with other constructors. This makes the interface more explicit and less likely to cause naming conflicts.
|
||||
|
||||
## Object-Oriented Design
|
||||
|
||||
The Git module follows an object-oriented design with two main classes:
|
||||
|
||||
1. **GitTree** - Represents a collection of git repositories under a base path
|
||||
- Created with `gittree_new(base_path)`
|
||||
- Methods for listing, finding, and getting repositories
|
||||
|
||||
2. **GitRepo** - Represents a single git repository
|
||||
- Obtained from GitTree's `get()` method
|
||||
- Methods for common git operations: pull, reset, push, commit
|
||||
|
||||
This design allows for a more intuitive and flexible interface, with method chaining for complex operations.
|
||||
|
||||
## Creating a GitTree
|
||||
|
||||
The GitTree object is the main entry point for git operations. It represents a collection of git repositories under a base path.
|
||||
|
||||
```js
|
||||
// Create a new GitTree with a base path
|
||||
let git_tree = gittree_new("/root/code");
|
||||
print(`Created GitTree with base path: /home/user/code`);
|
||||
```
|
||||
|
||||
## Finding Repositories
|
||||
|
||||
### List All Repositories
|
||||
|
||||
```js
|
||||
// List all git repositories under the base path
|
||||
let repos = git_tree.list();
|
||||
print(`Found ${repos.len()} repositories`);
|
||||
|
||||
// Print the repositories
|
||||
for repo in repos {
|
||||
print(` - ${repo}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Find Repositories Matching a Pattern
|
||||
|
||||
```js
|
||||
// Find repositories matching a pattern
|
||||
// Use a wildcard (*) suffix to find multiple matches
|
||||
let matching_repos = git_tree.find("my-project*");
|
||||
print("Matching repositories:");
|
||||
for repo in matching_repos {
|
||||
print(` - ${repo}`);
|
||||
}
|
||||
|
||||
// Find a specific repository (must match exactly one)
|
||||
let specific_repo = git_tree.find("unique-project")[0];
|
||||
print(`Found specific repository: ${specific_repo}`);
|
||||
```
|
||||
|
||||
## Working with Repositories
|
||||
|
||||
### Get Repository Objects
|
||||
|
||||
```js
|
||||
// Get GitRepo objects for repositories matching a pattern
|
||||
let repos = git_tree.get("my-project*");
|
||||
print(`Found ${repos.len()} repositories`);
|
||||
|
||||
// Get a specific repository
|
||||
let repo = git_tree.get("unique-project")[0];
|
||||
print(`Working with repository: ${repo.path()}`);
|
||||
```
|
||||
|
||||
### Clone a Repository
|
||||
|
||||
```js
|
||||
// Clone a repository by URL
|
||||
// This will clone the repository to the base path of the GitTree
|
||||
let repos = git_tree.get("https://github.com/username/repo.git");
|
||||
let repo = repos[0];
|
||||
print(`Repository cloned to: ${repo.path()}`);
|
||||
```
|
||||
|
||||
### Check for Changes
|
||||
|
||||
```js
|
||||
// Check if a repository has uncommitted changes
|
||||
let repo = git_tree.get("my-project")[0];
|
||||
if repo.has_changes() {
|
||||
print("Repository has uncommitted changes");
|
||||
} else {
|
||||
print("Repository is clean");
|
||||
}
|
||||
```
|
||||
|
||||
## Repository Operations
|
||||
|
||||
### Pull Changes
|
||||
|
||||
```js
|
||||
// Pull the latest changes from the remote
|
||||
// This will fail if there are uncommitted changes
|
||||
let repo = git_tree.get("my-project")[0];
|
||||
let result = repo.pull();
|
||||
print("Repository updated successfully");
|
||||
```
|
||||
|
||||
### Reset Local Changes
|
||||
|
||||
```js
|
||||
// Reset any local changes in the repository
|
||||
let repo = git_tree.get("my-project")[0];
|
||||
let result = repo.reset();
|
||||
print("Repository reset successfully");
|
||||
```
|
||||
|
||||
### Commit Changes
|
||||
|
||||
```js
|
||||
// Commit changes in the repository
|
||||
let repo = git_tree.get("my-project")[0];
|
||||
let result = repo.commit("Fix bug in login form");
|
||||
print("Changes committed successfully");
|
||||
```
|
||||
|
||||
### Push Changes
|
||||
|
||||
```js
|
||||
// Push changes to the remote
|
||||
let repo = git_tree.get("my-project")[0];
|
||||
let result = repo.push();
|
||||
print("Changes pushed successfully");
|
||||
```
|
||||
|
||||
## Method Chaining
|
||||
|
||||
The GitRepo methods can be chained together for more complex operations:
|
||||
|
||||
```js
|
||||
// Commit changes and push them to the remote
|
||||
let repo = git_tree.get("my-project")[0];
|
||||
let result = repo.commit("Add new feature").push();
|
||||
print("Changes committed and pushed successfully");
|
||||
|
||||
// Reset local changes, pull the latest changes, and commit new changes
|
||||
let repo = git_tree.get("my-project")[0];
|
||||
let result = repo.reset().pull().commit("Update dependencies");
|
||||
print("Repository updated successfully");
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
```js
|
||||
// Create a new GitTree
|
||||
let home_dir = env("HOME");
|
||||
let git_tree = gittree_new(`${home_dir}/code`);
|
||||
|
||||
// Clone a repository
|
||||
let repos = git_tree.get("https://github.com/username/example-repo.git");
|
||||
let repo = repos[0];
|
||||
print(`Cloned repository to: ${repo.path()}`);
|
||||
|
||||
// Make some changes (using OS module functions)
|
||||
let file_path = `${repo.path()}/README.md`;
|
||||
let content = "# Example Repository\n\nThis is an example repository.";
|
||||
write_file(file_path, content);
|
||||
|
||||
// Commit and push the changes
|
||||
let result = repo.commit("Update README.md").push();
|
||||
print("Changes committed and pushed successfully");
|
||||
|
||||
// List all repositories
|
||||
let all_repos = git_tree.list();
|
||||
print("All repositories:");
|
||||
for repo_path in all_repos {
|
||||
print(` - ${repo_path}`);
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
All methods in the Git module return a Result type, which means they can either succeed or fail with an error. If an error occurs, it will be propagated to the HeroScript script as a runtime error.
|
||||
|
||||
For example, if you try to clone a repository that doesn't exist:
|
||||
|
||||
```js
|
||||
// Try to clone a non-existent repository
|
||||
try {
|
||||
let git_tree = gittree_new("/root/code");
|
||||
let repos = git_tree.get("https://github.com/nonexistent/repo.git");
|
||||
print("This will not be executed if the repository doesn't exist");
|
||||
} catch(err) {
|
||||
print(`Error: ${err}`); // Will print the error message from git
|
||||
}
|
||||
```
|
||||
|
||||
Common errors include:
|
||||
- Invalid URL
|
||||
- Repository not found
|
||||
- Authentication failure
|
||||
- Network issues
|
||||
- Local changes exist when trying to pull
|
@@ -1,10 +0,0 @@
|
||||
---
|
||||
title: "intro"
|
||||
sidebar_position: 1
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
## HeroScript Script Commands Documentation
|
||||
|
||||
|
||||
The SAL library provides integration with the HeroScript scripting language, allowing you to use powerful system functions within your HeroScript scripts. These functions are organized into modules that provide related functionality.
|
@@ -1,239 +0,0 @@
|
||||
---
|
||||
title: "run containers"
|
||||
sidebar_position: 21
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Container Manager
|
||||
|
||||
The Container Manager module provides a comprehensive API for working with containers using nerdctl. It offers a modern builder pattern approach for container management.
|
||||
|
||||
## Container Builder Pattern
|
||||
|
||||
The Container Builder Pattern allows for fluent, chainable configuration of containers. This pattern makes container creation more readable and maintainable by allowing you to build complex container configurations step by step.
|
||||
|
||||
### Creating a Container
|
||||
|
||||
Start by creating a new container instance:
|
||||
|
||||
```rhai
|
||||
// Create an empty container with just a name
|
||||
let container = nerdctl_container_new("my-container");
|
||||
|
||||
// Or create a container from an image
|
||||
let container = nerdctl_container_from_image("my-container", "nginx:latest");
|
||||
```
|
||||
|
||||
### Configuring the Container
|
||||
|
||||
Once you have a container instance, you can configure it using the various builder methods:
|
||||
|
||||
```rhai
|
||||
// Configure the container with various options
|
||||
let container = nerdctl_container_from_image("web-server", "nginx:latest")
|
||||
.with_port("8080:80") // Map port 8080 to container port 80
|
||||
.with_volume("/host/path:/container/path") // Mount a volume
|
||||
.with_env("NGINX_HOST", "localhost") // Set an environment variable
|
||||
.with_network("bridge") // Set the network
|
||||
.with_detach(true); // Run in detached mode
|
||||
```
|
||||
|
||||
### Resetting Container Configuration
|
||||
|
||||
If you need to reset the container configuration to its default state while keeping the name and image:
|
||||
|
||||
```rhai
|
||||
// Reset the container configuration
|
||||
let container = nerdctl_container_from_image("web-server", "nginx:latest")
|
||||
.reset() // Reset all configuration to defaults
|
||||
.with_port("8080:80") // Start configuring again
|
||||
.with_detach(true);
|
||||
```
|
||||
|
||||
### Building and Starting the Container
|
||||
|
||||
After configuring the container, you can build and start it:
|
||||
|
||||
```rhai
|
||||
// Build the container (creates it but doesn't start it)
|
||||
let built_container = container.build();
|
||||
|
||||
// Start the container
|
||||
let start_result = built_container.start();
|
||||
|
||||
// Check if the container started successfully
|
||||
if (start_result.success) {
|
||||
println("Container started successfully!");
|
||||
} else {
|
||||
println(`Failed to start container: ${start_result.stderr}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Container Lifecycle Operations
|
||||
|
||||
Once your container is running, you can perform various operations:
|
||||
|
||||
```rhai
|
||||
// Execute a command in the container
|
||||
let exec_result = container.exec("ls -la");
|
||||
|
||||
// Get container logs
|
||||
let logs = container.logs();
|
||||
|
||||
// Stop the container
|
||||
let stop_result = container.stop();
|
||||
|
||||
// Remove the container
|
||||
let remove_result = container.remove();
|
||||
```
|
||||
|
||||
## Available Builder Methods
|
||||
|
||||
The Container Builder Pattern provides the following methods for configuring containers:
|
||||
|
||||
| Method | Description | Example |
|
||||
| -------------------------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------- |
|
||||
| `reset()` | Reset configuration to defaults | `.reset()` |
|
||||
| `with_port(port)` | Add a port mapping | `.with_port("8080:80")` |
|
||||
| `with_ports(ports_array)` | Add multiple port mappings | `.with_ports(["8080:80", "443:443"])` |
|
||||
| `with_volume(volume)` | Add a volume mount | `.with_volume("/host/path:/container/path")` |
|
||||
| `with_volumes(volumes_array)` | Add multiple volume mounts | `.with_volumes(["/host/path1:/container/path1", "/host/path2:/container/path2"])` |
|
||||
| `with_env(key, value)` | Add an environment variable | `.with_env("NGINX_HOST", "localhost")` |
|
||||
| `with_envs(env_map)` | Add multiple environment variables | `.with_envs(#{"KEY1": "value1", "KEY2": "value2"})` |
|
||||
| `with_network(network)` | Set the network | `.with_network("bridge")` |
|
||||
| `with_network_alias(alias)` | Add a network alias | `.with_network_alias("web-server")` |
|
||||
| `with_network_aliases(aliases_array)` | Add multiple network aliases | `.with_network_aliases(["web-server", "http-service"])` |
|
||||
| `with_cpu_limit(cpus)` | Set CPU limit | `.with_cpu_limit("1.0")` |
|
||||
| `with_cpu_shares(shares)` | Set CPU shares | `.with_cpu_shares("1024")` |
|
||||
| `with_memory_limit(memory)` | Set memory limit | `.with_memory_limit("512m")` |
|
||||
| `with_memory_swap_limit(memory_swap)` | Set memory swap limit | `.with_memory_swap_limit("1g")` |
|
||||
| `with_restart_policy(policy)` | Set restart policy | `.with_restart_policy("unless-stopped")` |
|
||||
| `with_health_check(cmd)` | Set health check command | `.with_health_check("curl -f http://localhost/ | | exit 1")` |
|
||||
| `with_health_check_options(cmd, interval, timeout, retries, start_period)` | Set health check with options | `.with_health_check_options("curl -f http://localhost/ | | exit 1", "5s", "3s", 3, "10s")` |
|
||||
| `with_snapshotter(snapshotter)` | Set snapshotter | `.with_snapshotter("native")` |
|
||||
| `with_detach(detach)` | Set detach mode | `.with_detach(true)` |
|
||||
|
||||
## Complete Example: Web Server
|
||||
|
||||
Here's a complete example that demonstrates setting up an Nginx web server using the Container Builder Pattern:
|
||||
|
||||
```rhai
|
||||
// Create a temporary directory for our files
|
||||
let work_dir = "/tmp/nerdctl";
|
||||
mkdir(work_dir);
|
||||
chdir(work_dir);
|
||||
|
||||
// Create a custom index.html file
|
||||
let html_content = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Rhai Nerdctl Demo</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 40px;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
h1 {
|
||||
color: #0066cc;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello from Rhai Nerdctl!</h1>
|
||||
<p>This page is served by an Nginx container created using the Rhai nerdctl wrapper.</p>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
// Write the HTML file
|
||||
let html_file = `${work_dir}/index.html`;
|
||||
file_write(html_file, html_content);
|
||||
|
||||
// Set up environment variables
|
||||
let env_map = #{};
|
||||
env_map["NGINX_HOST"] = "localhost";
|
||||
env_map["NGINX_PORT"] = "80";
|
||||
env_map["NGINX_WORKER_PROCESSES"] = "auto";
|
||||
|
||||
// Create and configure the container
|
||||
let container_name = "rhai-nginx-demo";
|
||||
|
||||
// First, try to remove any existing container with the same name
|
||||
nerdctl_remove(container_name);
|
||||
|
||||
// Create a container with a rich set of options using the builder pattern
|
||||
let container = nerdctl_container_from_image(container_name, "nginx:latest")
|
||||
.reset() // Reset to default configuration
|
||||
.with_detach(true)
|
||||
.with_ports(["8080:80"]) // Add multiple ports at once
|
||||
.with_volumes([`${work_dir}:/usr/share/nginx/html`]) // Mount our work dir
|
||||
.with_envs(env_map) // Add multiple environment variables at once
|
||||
.with_network("bridge")
|
||||
.with_network_aliases(["web-server", "nginx-demo"]) // Add multiple network aliases
|
||||
.with_cpu_limit("1.0")
|
||||
.with_memory_limit("512m");
|
||||
|
||||
// Build and start the container
|
||||
let built_container = container.build();
|
||||
let start_result = built_container.start();
|
||||
|
||||
println("The web server is running at http://localhost:8080");
|
||||
```
|
||||
|
||||
## Using Local Images Created with Buildah
|
||||
|
||||
When working with images created by Buildah, you may need to take additional steps to ensure nerdctl can find and use these images. This is because Buildah and nerdctl may use different storage backends by default.
|
||||
|
||||
### Tagging with localhost Prefix
|
||||
|
||||
One approach is to tag the Buildah-created image with a `localhost/` prefix:
|
||||
|
||||
```rhai
|
||||
// Create and commit a container with Buildah
|
||||
let builder = bah_new("my-container", "alpine:latest");
|
||||
builder.run("echo 'Hello' > /hello.txt");
|
||||
builder.commit("my-custom-image:latest");
|
||||
|
||||
// Tag the image with localhost prefix for nerdctl compatibility
|
||||
let local_image_name = "localhost/my-custom-image:latest";
|
||||
bah_image_tag("my-custom-image:latest", local_image_name);
|
||||
|
||||
// Now use the image with nerdctl
|
||||
let container = nerdctl_container_from_image("my-app", local_image_name)
|
||||
.with_detach(true)
|
||||
.build();
|
||||
```
|
||||
|
||||
### Using a Local Registry
|
||||
|
||||
For more reliable interoperability, you can push the image to a local registry:
|
||||
|
||||
```rhai
|
||||
// Push the Buildah-created image to a local registry
|
||||
bah_image_push("my-custom-image:latest", "localhost:5000/my-custom-image:latest", false);
|
||||
|
||||
// Pull the image with nerdctl
|
||||
nerdctl_image_pull("localhost:5000/my-custom-image:latest");
|
||||
|
||||
// Use the image
|
||||
let container = nerdctl_container_from_image("my-app", "localhost:5000/my-custom-image:latest")
|
||||
.with_detach(true)
|
||||
.build();
|
||||
```
|
||||
|
||||
## Image Management Functions
|
||||
|
||||
The module also provides functions for managing container images:
|
||||
|
||||
| Function | Description | Example |
|
||||
| --------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| `nerdctl_images()` | List images in local storage | `nerdctl_images()` |
|
||||
| `nerdctl_image_remove(image)` | Remove an image | `nerdctl_image_remove("nginx:latest")` |
|
||||
| `nerdctl_image_push(image, destination)` | Push an image to a registry | `nerdctl_image_push("my-image:latest", "registry.example.com/my-image:latest")` |
|
||||
| `nerdctl_image_tag(image, new_name)` | Add an additional name to a local image | `nerdctl_image_tag("nginx:latest", "my-nginx:latest")` |
|
||||
| `nerdctl_image_pull(image)` | Pull an image from a registry | `nerdctl_image_pull("nginx:latest")` |
|
||||
| `nerdctl_image_commit(container, image_name)` | Commit a container to an image | `nerdctl_image_commit("web-server", "my-nginx:latest")` |
|
||||
| `nerdctl_image_build(tag, context_path)` | Build an image using a Dockerfile | `nerdctl_image_build("my-image:latest", "./")` |
|
@@ -1,359 +0,0 @@
|
||||
---
|
||||
title: "os"
|
||||
sidebar_position: 2
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# OS Tools
|
||||
|
||||
The OS module provides functions for working with files, directories, and downloading files from the internet.
|
||||
|
||||
## File System Functions
|
||||
|
||||
### `copy(src, dest)`
|
||||
|
||||
Recursively copies a file or directory from source to destination.
|
||||
|
||||
**Parameters:**
|
||||
- `src` (string): The source file or directory path
|
||||
- `dest` (string): The destination path
|
||||
|
||||
**Returns:** A message confirming the copy was successful.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Copy a file
|
||||
copy("source.txt", "destination.txt");
|
||||
|
||||
// Copy a directory recursively
|
||||
copy("source_dir", "destination_dir");
|
||||
```
|
||||
|
||||
### `exist(path)`
|
||||
|
||||
Checks if a file or directory exists.
|
||||
|
||||
**Parameters:**
|
||||
- `path` (string): The path to check
|
||||
|
||||
**Returns:** A boolean value - `true` if the file or directory exists, `false` otherwise.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
if exist("config.json") {
|
||||
// File exists, do something
|
||||
} else {
|
||||
// File doesn't exist
|
||||
}
|
||||
```
|
||||
|
||||
### `find_file(dir, filename)`
|
||||
|
||||
Finds a file in a directory with support for wildcards.
|
||||
|
||||
**Parameters:**
|
||||
- `dir` (string): The directory to search in
|
||||
- `filename` (string): The filename pattern to search for (supports wildcards)
|
||||
|
||||
**Returns:** The path of the first matching file.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Find a specific file
|
||||
let config_file = find_file("./config", "settings.json");
|
||||
|
||||
// Find using wildcards
|
||||
let log_file = find_file("./logs", "*.log");
|
||||
```
|
||||
|
||||
### `find_files(dir, filename)`
|
||||
|
||||
Finds multiple files in a directory recursively with support for wildcards.
|
||||
|
||||
**Parameters:**
|
||||
- `dir` (string): The directory to search in
|
||||
- `filename` (string): The filename pattern to search for (supports wildcards)
|
||||
|
||||
**Returns:** A list of matching file paths.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Find all JSON files
|
||||
let json_files = find_files("./data", "*.json");
|
||||
|
||||
// Process each file
|
||||
for file in json_files {
|
||||
print(`Found file: ${file}`);
|
||||
}
|
||||
```
|
||||
|
||||
### `find_dir(dir, dirname)`
|
||||
|
||||
Finds a directory in a parent directory with support for wildcards.
|
||||
|
||||
**Parameters:**
|
||||
- `dir` (string): The parent directory to search in
|
||||
- `dirname` (string): The directory name pattern to search for (supports wildcards)
|
||||
|
||||
**Returns:** The path of the first matching directory.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Find a specific directory
|
||||
let config_dir = find_dir("./", "config");
|
||||
|
||||
// Find using wildcards
|
||||
let version_dir = find_dir("./releases", "v*");
|
||||
```
|
||||
|
||||
### `find_dirs(dir, dirname)`
|
||||
|
||||
Finds multiple directories in a parent directory recursively with support for wildcards.
|
||||
|
||||
**Parameters:**
|
||||
- `dir` (string): The parent directory to search in
|
||||
- `dirname` (string): The directory name pattern to search for (supports wildcards)
|
||||
|
||||
**Returns:** A list of matching directory paths.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Find all version directories
|
||||
let version_dirs = find_dirs("./releases", "v*");
|
||||
|
||||
// Process each directory
|
||||
for dir in version_dirs {
|
||||
print(`Found directory: ${dir}`);
|
||||
}
|
||||
```
|
||||
|
||||
### `delete(path)`
|
||||
|
||||
Deletes a file or directory. This function is defensive and doesn't error if the file doesn't exist.
|
||||
|
||||
**Parameters:**
|
||||
- `path` (string): The path of the file or directory to delete
|
||||
|
||||
**Returns:** A message confirming the deletion was successful.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Delete a file
|
||||
delete("temp.txt");
|
||||
|
||||
// Delete a directory
|
||||
delete("temp_dir");
|
||||
```
|
||||
|
||||
### `mv(src, dest)`
|
||||
|
||||
Moves a file or directory from source to destination.
|
||||
|
||||
**Parameters:**
|
||||
- `src` (string): The source path
|
||||
- `dest` (string): The destination path
|
||||
|
||||
**Returns:** A message confirming the move was successful.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Move a file
|
||||
mv("file.txt", "new_location/file.txt");
|
||||
|
||||
// Move a directory
|
||||
mv("source_dir", "destination_dir");
|
||||
|
||||
// Rename a file
|
||||
mv("old_name.txt", "new_name.txt");
|
||||
```
|
||||
|
||||
### `mkdir(path)`
|
||||
|
||||
Creates a directory and all parent directories. This function is defensive and doesn't error if the directory already exists.
|
||||
|
||||
**Parameters:**
|
||||
- `path` (string): The path of the directory to create
|
||||
|
||||
**Returns:** A message confirming the directory was created.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Create a directory
|
||||
mkdir("new_dir");
|
||||
|
||||
// Create nested directories
|
||||
mkdir("parent/child/grandchild");
|
||||
```
|
||||
|
||||
### `file_size(path)`
|
||||
|
||||
Gets the size of a file in bytes.
|
||||
|
||||
**Parameters:**
|
||||
- `path` (string): The path of the file
|
||||
|
||||
**Returns:** The size of the file in bytes.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Get file size
|
||||
let size = file_size("large_file.dat");
|
||||
print(`File size: ${size} bytes`);
|
||||
```
|
||||
|
||||
## File Content Functions
|
||||
|
||||
### `file_read(path)`
|
||||
|
||||
Reads the contents of a file.
|
||||
|
||||
**Parameters:**
|
||||
- `path` (string): The path of the file to read
|
||||
|
||||
**Returns:** The content of the file as a string.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Read a file
|
||||
let content = file_read("config.json");
|
||||
print(`File content: ${content}`);
|
||||
```
|
||||
|
||||
### `file_write(path, content)`
|
||||
|
||||
Writes content to a file. Creates the file if it doesn't exist, overwrites if it does.
|
||||
|
||||
**Parameters:**
|
||||
- `path` (string): The path of the file to write to
|
||||
- `content` (string): The content to write to the file
|
||||
|
||||
**Returns:** A message confirming the file was written.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Write to a file
|
||||
file_write("config.json", "{\n \"setting\": \"value\"\n}");
|
||||
```
|
||||
|
||||
### `file_write_append(path, content)`
|
||||
|
||||
Appends content to a file. Creates the file if it doesn't exist.
|
||||
|
||||
**Parameters:**
|
||||
- `path` (string): The path of the file to append to
|
||||
- `content` (string): The content to append to the file
|
||||
|
||||
**Returns:** A message confirming the content was appended.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Append to a log file
|
||||
file_write_append("log.txt", "New log entry\n");
|
||||
```
|
||||
|
||||
### `rsync(src, dest)`
|
||||
|
||||
Syncs directories using rsync (or platform equivalent).
|
||||
|
||||
**Parameters:**
|
||||
- `src` (string): The source directory
|
||||
- `dest` (string): The destination directory
|
||||
|
||||
**Returns:** A message confirming the directories were synced.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Sync directories
|
||||
rsync("source_dir", "backup_dir");
|
||||
```
|
||||
|
||||
### `chdir(path)`
|
||||
|
||||
Changes the current working directory.
|
||||
|
||||
**Parameters:**
|
||||
- `path` (string): The path to change to
|
||||
|
||||
**Returns:** A message confirming the directory was changed.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Change directory
|
||||
chdir("project/src");
|
||||
```
|
||||
|
||||
## Download Functions
|
||||
|
||||
### `download(url, dest, min_size_kb)`
|
||||
|
||||
Downloads a file from a URL to a destination directory using the curl command. If the URL ends with a supported archive format, the file will be automatically extracted to the destination directory.
|
||||
|
||||
**Supported archive formats for automatic extraction:**
|
||||
- `.tar.gz`
|
||||
- `.tgz`
|
||||
- `.tar`
|
||||
- `.zip`
|
||||
|
||||
**Parameters:**
|
||||
- `url` (string): The URL to download from
|
||||
- `dest` (string): The destination directory where the file will be saved or extracted
|
||||
- `min_size_kb` (integer): The minimum expected file size in kilobytes (for validation)
|
||||
|
||||
**Returns:** The path where the file was saved or extracted.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Download a file to a directory
|
||||
download("https://example.com/file.zip", "downloads/", 10);
|
||||
```
|
||||
|
||||
### `download_file(url, dest, min_size_kb)`
|
||||
|
||||
Downloads a file from a URL to a specific file destination using the curl command. This function is designed for downloading files to a specific path, not for extracting archives.
|
||||
|
||||
**Parameters:**
|
||||
- `url` (string): The URL to download from
|
||||
- `dest` (string): The destination file path where the file will be saved
|
||||
- `min_size_kb` (integer): The minimum expected file size in kilobytes (for validation)
|
||||
|
||||
**Returns:** The path where the file was saved.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Download a file to a specific path
|
||||
download_file("https://example.com/file.txt", "downloads/myfile.txt", 10);
|
||||
```
|
||||
|
||||
### `download_install(url, min_size_kb)`
|
||||
|
||||
Downloads a file and installs it if it's a supported package format.
|
||||
|
||||
**Supported package formats for automatic installation:**
|
||||
- `.deb` packages on Debian-based systems
|
||||
|
||||
**Parameters:**
|
||||
- `url` (string): The URL to download from
|
||||
- `min_size_kb` (integer): The minimum expected file size in kilobytes (for validation)
|
||||
|
||||
**Returns:** The path where the file was saved or installed.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Download and install a package
|
||||
download_install("https://example.com/package.deb", 1000);
|
||||
```
|
||||
|
||||
### `chmod_exec(path)`
|
||||
|
||||
Makes a file executable (equivalent to `chmod +x` in Unix).
|
||||
|
||||
**Parameters:**
|
||||
- `path` (string): The path to the file to make executable
|
||||
|
||||
**Returns:** A message confirming the file was made executable.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Make a file executable
|
||||
chmod_exec("downloads/script.sh");
|
||||
```
|
@@ -1,237 +0,0 @@
|
||||
---
|
||||
title: "process"
|
||||
sidebar_position: 3
|
||||
hide_title: true
|
||||
---
|
||||
|
||||
# Process Module
|
||||
|
||||
The Process module provides functions for running commands and managing processes on your system.
|
||||
|
||||
## Command Results
|
||||
|
||||
### Command Output Information
|
||||
|
||||
When you run a command, you get back information about what happened:
|
||||
|
||||
- `stdout`: The normal output of the command
|
||||
- `stderr`: Any error messages from the command
|
||||
- `success`: Whether the command worked (true) or failed (false)
|
||||
- `code`: The exit code (0 usually means success)
|
||||
|
||||
### Process Information
|
||||
|
||||
When you get information about a running process, you can see:
|
||||
|
||||
- `pid`: The process ID number
|
||||
- `name`: The name of the process
|
||||
- `memory`: How much memory the process is using
|
||||
- `cpu`: How much CPU the process is using
|
||||
|
||||
## Run Functions
|
||||
### `run(command)`
|
||||
|
||||
Runs a command or multiline script with arguments.
|
||||
|
||||
**Parameters:**
|
||||
- `command` (string): The command to run (can be a single command or a multiline script)
|
||||
|
||||
**Returns:** The result of the command, including output and whether it succeeded.
|
||||
|
||||
**Example 1: Running a simple command**
|
||||
```js
|
||||
// Run a simple command
|
||||
let result = run("ls -la");
|
||||
|
||||
// Check if the command was successful
|
||||
if result.success {
|
||||
print(`Command output: ${result.stdout}`);
|
||||
} else {
|
||||
print(`Command failed with error: ${result.stderr}`);
|
||||
}
|
||||
```
|
||||
|
||||
**Example 2: Running a multiline script**
|
||||
```js
|
||||
// Create a multiline script using backtick string literals
|
||||
let setup_script = `
|
||||
# Create directories
|
||||
mkdir -p /tmp/test_project
|
||||
cd /tmp/test_project
|
||||
|
||||
# Initialize git repository
|
||||
git init
|
||||
echo 'Initial content' > README.md
|
||||
git add README.md
|
||||
git config --local user.email 'test@example.com'
|
||||
git config --local user.name 'Test User'
|
||||
git commit -m 'Initial commit'
|
||||
`;
|
||||
|
||||
// Execute the multiline script
|
||||
let result = run(setup_script);
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
### `run_silent(command)`
|
||||
|
||||
Runs a command or multiline script with arguments silently (without displaying output).
|
||||
|
||||
**Parameters:**
|
||||
- `command` (string): The command to run
|
||||
|
||||
**Returns:** The result of the command, without displaying the output.
|
||||
|
||||
**Example:**
|
||||
|
||||
```js
|
||||
// Run a command silently
|
||||
let result = run_silent("git pull");
|
||||
|
||||
// Check the exit code
|
||||
if result.code == 0 {
|
||||
print("Git pull successful");
|
||||
} else {
|
||||
print(`Git pull failed with code ${result.code}`);
|
||||
}
|
||||
```
|
||||
|
||||
### `new_run_options()`
|
||||
|
||||
Creates a new map with default run options.
|
||||
|
||||
**Returns:** A map with the following default options:
|
||||
- `die` (boolean): `true` - Whether to throw an error if the command fails
|
||||
- `silent` (boolean): `false` - Whether to suppress command output
|
||||
- `async_exec` (boolean): `false` - Whether to run the command asynchronously
|
||||
- `log` (boolean): `false` - Whether to log the command execution
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Create run options
|
||||
let options = new_run_options();
|
||||
```
|
||||
|
||||
### `run_with_options(command, options)`
|
||||
|
||||
Runs a command with options specified in a map.
|
||||
|
||||
**Parameters:**
|
||||
- `command` (string): The command to run
|
||||
- `options` (map): A map of options created with `new_run_options()`
|
||||
|
||||
**Returns:** The result of the command with your custom settings applied.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Create and customize run options
|
||||
let options = new_run_options();
|
||||
options.die = false; // Don't throw an error if the command fails
|
||||
options.silent = true; // Suppress command output
|
||||
options.async_exec = false; // Run synchronously
|
||||
options.log = true; // Log the command execution
|
||||
|
||||
// Run a command with options
|
||||
let result = run_with_options("npm install", options);
|
||||
```
|
||||
|
||||
## Working with Multiline Scripts
|
||||
|
||||
The Process module allows you to execute multiline scripts, which is particularly useful for complex operations that require multiple commands to be executed in sequence.
|
||||
|
||||
### Creating Multiline Scripts
|
||||
|
||||
Multiline scripts can be created using backtick (`) string literals in HeroScript:
|
||||
|
||||
```js
|
||||
let my_script = `
|
||||
# This is a multiline bash script
|
||||
echo "Hello, World!"
|
||||
mkdir -p /tmp/my_project
|
||||
cd /tmp/my_project
|
||||
touch example.txt
|
||||
`;
|
||||
```
|
||||
|
||||
## Process Management Functions
|
||||
|
||||
### `which(cmd)`
|
||||
|
||||
Checks if a command exists in the PATH.
|
||||
|
||||
**Parameters:**
|
||||
- `cmd` (string): The command to check
|
||||
|
||||
**Returns:** The full path to the command if found, or nothing if not found.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Check if a command exists
|
||||
let git_path = which("git");
|
||||
|
||||
if git_path != () {
|
||||
print(`Git is installed at: ${git_path}`);
|
||||
} else {
|
||||
print("Git is not installed");
|
||||
}
|
||||
```
|
||||
|
||||
### `kill(pattern)`
|
||||
|
||||
Kills processes matching a pattern.
|
||||
|
||||
**Parameters:**
|
||||
- `pattern` (string): The pattern to match process names against
|
||||
|
||||
**Returns:** A message confirming the processes were killed.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Kill all processes with "node" in their name
|
||||
kill("node");
|
||||
```
|
||||
|
||||
### `process_list(pattern)`
|
||||
|
||||
Lists processes matching a pattern (or all processes if the pattern is empty).
|
||||
|
||||
**Parameters:**
|
||||
- `pattern` (string): The pattern to match process names against (can be empty to list all processes)
|
||||
|
||||
**Returns:** A list of processes matching your search.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// List all processes
|
||||
let all_processes = process_list("");
|
||||
|
||||
// List processes containing "node" in their name
|
||||
let node_processes = process_list("node");
|
||||
|
||||
// Display process information
|
||||
for process in node_processes {
|
||||
print(`PID: ${process.pid}, Name: ${process.name}, Memory: ${process.memory}, CPU: ${process.cpu}`);
|
||||
}
|
||||
```
|
||||
|
||||
### `process_get(pattern)`
|
||||
|
||||
Gets a single process matching the pattern. Throws an error if zero or more than one process matches.
|
||||
|
||||
**Parameters:**
|
||||
- `pattern` (string): The pattern to match process names against
|
||||
|
||||
**Returns:** Information about the matching process. This will only work if exactly one process matches.
|
||||
|
||||
**Example:**
|
||||
```js
|
||||
// Try to get a specific process
|
||||
try {
|
||||
let process = process_get("my_app");
|
||||
print(`Found process: PID=${process.pid}, Name=${process.name}`);
|
||||
} catch(err) {
|
||||
print(`Error: ${err}`);
|
||||
}
|
||||
```
|
@@ -1,154 +0,0 @@
|
||||
# RFS (Remote File System)
|
||||
|
||||
The RFS module provides a Rust wrapper for the RFS tool, which allows mounting remote filesystems locally and managing filesystem layers.
|
||||
|
||||
## Overview
|
||||
|
||||
RFS (Remote File System) is a tool that enables mounting various types of remote filesystems locally, as well as creating and managing filesystem layers. The SAL library provides a Rust wrapper for RFS with a fluent builder API, making it easy to use in your applications.
|
||||
|
||||
## Features
|
||||
|
||||
- Mount remote filesystems locally (SSH, S3, WebDAV, etc.)
|
||||
- List mounted filesystems
|
||||
- Unmount filesystems
|
||||
- Pack directories into filesystem layers
|
||||
- Unpack filesystem layers
|
||||
- List contents of filesystem layers
|
||||
- Verify filesystem layers
|
||||
|
||||
## Usage in Rust
|
||||
|
||||
### Mounting a Filesystem
|
||||
|
||||
```rust
|
||||
use sal::virt::rfs::{RfsBuilder, MountType};
|
||||
|
||||
// Create a new RFS builder
|
||||
let mount = RfsBuilder::new("user@example.com:/remote/path", "/local/mount/point", MountType::SSH)
|
||||
.with_option("port", "2222")
|
||||
.with_option("identity_file", "/path/to/key")
|
||||
.with_debug(true)
|
||||
.mount()?;
|
||||
|
||||
println!("Mounted filesystem with ID: {}", mount.id);
|
||||
```
|
||||
|
||||
### Listing Mounts
|
||||
|
||||
```rust
|
||||
use sal::virt::rfs::list_mounts;
|
||||
|
||||
// List all mounts
|
||||
let mounts = list_mounts()?;
|
||||
for mount in mounts {
|
||||
println!("Mount ID: {}, Source: {}, Target: {}", mount.id, mount.source, mount.target);
|
||||
}
|
||||
```
|
||||
|
||||
### Unmounting a Filesystem
|
||||
|
||||
```rust
|
||||
use sal::virt::rfs::unmount;
|
||||
|
||||
// Unmount a filesystem
|
||||
unmount("/local/mount/point")?;
|
||||
```
|
||||
|
||||
### Packing a Directory
|
||||
|
||||
```rust
|
||||
use sal::virt::rfs::{PackBuilder, StoreSpec};
|
||||
|
||||
// Create store specifications
|
||||
let store_spec = StoreSpec::new("file")
|
||||
.with_option("path", "/path/to/store");
|
||||
|
||||
// Pack a directory with builder pattern
|
||||
let result = PackBuilder::new("/path/to/directory", "output.fl")
|
||||
.with_store_spec(store_spec)
|
||||
.with_debug(true)
|
||||
.pack()?;
|
||||
```
|
||||
|
||||
### Unpacking a Filesystem Layer
|
||||
|
||||
```rust
|
||||
use sal::virt::rfs::unpack;
|
||||
|
||||
// Unpack a filesystem layer
|
||||
unpack("input.fl", "/path/to/unpack")?;
|
||||
```
|
||||
|
||||
## Usage in Rhai Scripts
|
||||
|
||||
### Mounting a Filesystem
|
||||
|
||||
```rhai
|
||||
// Create a map for mount options
|
||||
let options = #{
|
||||
"port": "22",
|
||||
"identity_file": "/path/to/key",
|
||||
"readonly": "true"
|
||||
};
|
||||
|
||||
// Mount the directory
|
||||
let mount = rfs_mount("user@example.com:/remote/path", "/local/mount/point", "ssh", options);
|
||||
|
||||
print(`Mounted ${mount.source} to ${mount.target} with ID: ${mount.id}`);
|
||||
```
|
||||
|
||||
### Listing Mounts
|
||||
|
||||
```rhai
|
||||
// List all mounts
|
||||
let mounts = rfs_list_mounts();
|
||||
print(`Number of mounts: ${mounts.len()}`);
|
||||
|
||||
for mount in mounts {
|
||||
print(`Mount ID: ${mount.id}, Source: ${mount.source}, Target: ${mount.target}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Unmounting a Filesystem
|
||||
|
||||
```rhai
|
||||
// Unmount the directory
|
||||
rfs_unmount("/local/mount/point");
|
||||
```
|
||||
|
||||
### Packing a Directory
|
||||
|
||||
```rhai
|
||||
// Pack the directory
|
||||
// Store specs format: "file:path=/path/to/store,s3:bucket=my-bucket"
|
||||
rfs_pack("/path/to/directory", "output.fl", "file:path=/path/to/store");
|
||||
```
|
||||
|
||||
### Unpacking a Filesystem Layer
|
||||
|
||||
```rhai
|
||||
// Unpack the filesystem layer
|
||||
rfs_unpack("output.fl", "/path/to/unpack");
|
||||
```
|
||||
|
||||
## Mount Types
|
||||
|
||||
The RFS module supports various mount types:
|
||||
|
||||
- **Local**: Mount a local directory
|
||||
- **SSH**: Mount a remote directory via SSH
|
||||
- **S3**: Mount an S3 bucket
|
||||
- **WebDAV**: Mount a WebDAV server
|
||||
|
||||
## Store Specifications
|
||||
|
||||
When packing a directory into a filesystem layer, you can specify one or more stores to use. Each store has a type and options:
|
||||
|
||||
- **File**: Store files on the local filesystem
|
||||
- Options: `path` (path to the store)
|
||||
- **S3**: Store files in an S3 bucket
|
||||
- Options: `bucket` (bucket name), `region` (AWS region), `access_key`, `secret_key`
|
||||
|
||||
## Examples
|
||||
|
||||
See the [RFS example script](../../rhaiexamples/rfs_example.rhai) for more examples of how to use the RFS module in Rhai scripts.
|
@@ -1,237 +0,0 @@
|
||||
# Text Manipulation Tools
|
||||
|
||||
The SAL text module provides powerful text manipulation capabilities that can be used from Rhai scripts. These include text replacement (with regex support), template rendering, string normalization, and text formatting utilities.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Text Replacement](#text-replacement)
|
||||
- [Template Rendering](#template-rendering)
|
||||
- [String Normalization](#string-normalization)
|
||||
- [Text Formatting](#text-formatting)
|
||||
|
||||
## Text Replacement
|
||||
|
||||
The text replacement tools allow you to perform simple or complex text replacements, with support for regular expressions, case-insensitive matching, and file operations.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```rhai
|
||||
// Create a new text replacer
|
||||
let replacer = text_replacer_new()
|
||||
.pattern("foo") // Set the pattern to search for
|
||||
.replacement("bar") // Set the replacement text
|
||||
.build(); // Build the replacer
|
||||
|
||||
// Apply the replacer to a string
|
||||
let result = replacer.replace("foo bar foo");
|
||||
// Result: "bar bar bar"
|
||||
```
|
||||
|
||||
### Advanced Features
|
||||
|
||||
#### Regular Expressions
|
||||
|
||||
```rhai
|
||||
// Create a replacer with regex support
|
||||
let replacer = text_replacer_new()
|
||||
.pattern("\\bfoo\\b") // Use regex pattern (word boundary)
|
||||
.replacement("bar")
|
||||
.regex(true) // Enable regex mode
|
||||
.build();
|
||||
|
||||
// Apply the replacer to a string
|
||||
let result = replacer.replace("foo foobar");
|
||||
// Result: "bar foobar" (only replaces whole "foo" words)
|
||||
```
|
||||
|
||||
#### Case-Insensitive Matching
|
||||
|
||||
```rhai
|
||||
// Create a replacer with case-insensitive matching
|
||||
let replacer = text_replacer_new()
|
||||
.pattern("foo")
|
||||
.replacement("bar")
|
||||
.regex(true)
|
||||
.case_insensitive(true) // Enable case-insensitive matching
|
||||
.build();
|
||||
|
||||
// Apply the replacer to a string
|
||||
let result = replacer.replace("FOO foo Foo");
|
||||
// Result: "bar bar bar"
|
||||
```
|
||||
|
||||
#### Multiple Replacements
|
||||
|
||||
```rhai
|
||||
// Chain multiple replacements
|
||||
let replacer = text_replacer_new()
|
||||
.pattern("foo")
|
||||
.replacement("bar")
|
||||
.and() // Add another replacement operation
|
||||
.pattern("baz")
|
||||
.replacement("qux")
|
||||
.build();
|
||||
|
||||
// Apply the replacer to a string
|
||||
let result = replacer.replace("foo baz");
|
||||
// Result: "bar qux"
|
||||
```
|
||||
|
||||
#### File Operations
|
||||
|
||||
```rhai
|
||||
// Create a replacer
|
||||
let replacer = text_replacer_new()
|
||||
.pattern("foo")
|
||||
.replacement("bar")
|
||||
.build();
|
||||
|
||||
// Replace in a file and get the result as a string
|
||||
let result = replacer.replace_file("input.txt");
|
||||
|
||||
// Replace in a file and write back to the same file
|
||||
replacer.replace_file_in_place("input.txt");
|
||||
|
||||
// Replace in a file and write to a new file
|
||||
replacer.replace_file_to("input.txt", "output.txt");
|
||||
```
|
||||
|
||||
## Template Rendering
|
||||
|
||||
The template rendering tools allow you to create and render templates with variables, using the powerful Tera template engine.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```rhai
|
||||
// Create a template builder with a template file
|
||||
let template = template_builder_open("template.txt")
|
||||
.add_var("name", "John") // Add a string variable
|
||||
.add_var("age", 30) // Add a numeric variable
|
||||
.add_var("items", ["a", "b", "c"]); // Add an array variable
|
||||
|
||||
// Render the template
|
||||
let result = template.render();
|
||||
|
||||
// Render to a file
|
||||
template.render_to_file("output.txt");
|
||||
```
|
||||
|
||||
### Template Variables
|
||||
|
||||
You can add variables of various types:
|
||||
|
||||
```rhai
|
||||
let template = template_builder_open("template.txt")
|
||||
.add_var("name", "John") // String
|
||||
.add_var("age", 30) // Integer
|
||||
.add_var("height", 1.85) // Float
|
||||
.add_var("is_active", true) // Boolean
|
||||
.add_var("items", ["a", "b", "c"]); // Array
|
||||
```
|
||||
|
||||
### Using Map for Variables
|
||||
|
||||
```rhai
|
||||
// Create a map of variables
|
||||
let vars = #{
|
||||
name: "Alice",
|
||||
place: "Wonderland"
|
||||
};
|
||||
|
||||
// Add all variables from the map
|
||||
let template = template_builder_open("template.txt")
|
||||
.add_vars(vars);
|
||||
```
|
||||
|
||||
### Template Syntax
|
||||
|
||||
The template engine uses Tera, which supports:
|
||||
|
||||
- Variable interpolation: `{{ variable }}`
|
||||
- Conditionals: `{% if condition %}...{% endif %}`
|
||||
- Loops: `{% for item in items %}...{% endfor %}`
|
||||
- Filters: `{{ variable | filter }}`
|
||||
|
||||
Example template:
|
||||
|
||||
```
|
||||
Hello, {{ name }}!
|
||||
|
||||
{% if show_greeting %}
|
||||
Welcome to {{ place }}.
|
||||
{% endif %}
|
||||
|
||||
Your items:
|
||||
{% for item in items %}
|
||||
- {{ item }}{% if not loop.last %}{% endif %}
|
||||
{% endfor %}
|
||||
```
|
||||
|
||||
## String Normalization
|
||||
|
||||
The string normalization tools help convert strings to consistent formats for use as file names or paths.
|
||||
|
||||
### name_fix
|
||||
|
||||
Converts a string to a safe, normalized name by:
|
||||
- Converting to lowercase
|
||||
- Replacing spaces and special characters with underscores
|
||||
- Removing non-alphanumeric characters
|
||||
|
||||
```rhai
|
||||
let fixed_name = name_fix("Hello World!");
|
||||
// Result: "hello_world"
|
||||
|
||||
let fixed_name = name_fix("File-Name.txt");
|
||||
// Result: "file_name.txt"
|
||||
```
|
||||
|
||||
### path_fix
|
||||
|
||||
Similar to name_fix, but preserves path separators:
|
||||
|
||||
```rhai
|
||||
let fixed_path = path_fix("/path/to/Hello World!");
|
||||
// Result: "/path/to/hello_world"
|
||||
|
||||
let fixed_path = path_fix("./relative/path/to/DOCUMENT-123.pdf");
|
||||
// Result: "./relative/path/to/document_123.pdf"
|
||||
```
|
||||
|
||||
## Text Formatting
|
||||
|
||||
Tools to help with text formatting and indentation.
|
||||
|
||||
### dedent
|
||||
|
||||
Removes common leading whitespace from multi-line strings:
|
||||
|
||||
```rhai
|
||||
let indented_text = " line 1
|
||||
line 2
|
||||
line 3";
|
||||
|
||||
let dedented = dedent(indented_text);
|
||||
// Result: "line 1
|
||||
// line 2
|
||||
// line 3"
|
||||
```
|
||||
|
||||
### prefix
|
||||
|
||||
Adds a prefix to every line in a multi-line string:
|
||||
|
||||
```rhai
|
||||
let text = "line 1
|
||||
line 2
|
||||
line 3";
|
||||
|
||||
let prefixed = prefix(text, " ");
|
||||
// Result: " line 1
|
||||
// line 2
|
||||
// line 3"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
See the [text_tools.rhai](https://github.com/ourworld-tf/herocode/blob/main/sal/src/rhaiexamples/text_tools.rhai) example script for more detailed examples of using these text manipulation tools.
|
@@ -1,172 +0,0 @@
|
||||
// 01_builder_pattern.rhai
|
||||
// Tests for Buildah Builder pattern
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if buildah is available
|
||||
fn is_buildah_available() {
|
||||
try {
|
||||
let result = run("which buildah");
|
||||
return result.success;
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Buildah Builder Pattern ===");
|
||||
|
||||
// Check if buildah is available
|
||||
let buildah_available = is_buildah_available();
|
||||
if !buildah_available {
|
||||
print("Buildah is not available. Skipping Buildah tests.");
|
||||
// Exit gracefully without error
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ Buildah is available");
|
||||
|
||||
// Test creating a new Builder
|
||||
print("Testing bah_new()...");
|
||||
try {
|
||||
let builder = bah_new("rhai_test_container", "alpine:latest");
|
||||
|
||||
// Test Builder properties
|
||||
print("Testing Builder properties...");
|
||||
assert_true(builder.container_id != "", "Container ID should not be empty");
|
||||
assert_eq(builder.name, "rhai_test_container", "Container name should match");
|
||||
assert_eq(builder.image, "alpine:latest", "Image name should match");
|
||||
|
||||
// Test debug mode
|
||||
print("Testing debug mode...");
|
||||
assert_true(!builder.debug_mode, "Debug mode should be off by default");
|
||||
builder.debug_mode = true;
|
||||
assert_true(builder.debug_mode, "Debug mode should be on after setting");
|
||||
|
||||
// Test running a command
|
||||
print("Testing run()...");
|
||||
let result = builder.run("echo 'Hello from container'");
|
||||
assert_true(result.success, "Command should succeed");
|
||||
assert_true(result.stdout.contains("Hello from container"), "Command output should contain expected text");
|
||||
print("✓ run(): Command executed successfully");
|
||||
|
||||
// Test writing content to a file in the container
|
||||
print("Testing write_content()...");
|
||||
let content = "Hello from a file";
|
||||
builder.write_content(content, "/test_file.txt");
|
||||
|
||||
// Verify the content was written
|
||||
let read_result = builder.run("cat /test_file.txt");
|
||||
assert_true(read_result.success, "Command should succeed");
|
||||
assert_true(read_result.stdout.contains(content), "File content should match what was written");
|
||||
print("✓ write_content(): Content written successfully");
|
||||
|
||||
// Test reading content from a file in the container
|
||||
print("Testing read_content()...");
|
||||
let read_content = builder.read_content("/test_file.txt");
|
||||
assert_true(read_content.contains(content), "Read content should match what was written");
|
||||
print("✓ read_content(): Content read successfully");
|
||||
|
||||
// Test setting entrypoint
|
||||
print("Testing set_entrypoint()...");
|
||||
let entrypoint = ["/bin/sh", "-c"];
|
||||
builder.set_entrypoint(entrypoint);
|
||||
print("✓ set_entrypoint(): Entrypoint set successfully");
|
||||
|
||||
// Test setting cmd
|
||||
print("Testing set_cmd()...");
|
||||
let cmd = ["echo", "Hello from CMD"];
|
||||
builder.set_cmd(cmd);
|
||||
print("✓ set_cmd(): CMD set successfully");
|
||||
|
||||
// Test adding a file
|
||||
print("Testing add()...");
|
||||
// Create a test file
|
||||
file_write("test_add_file.txt", "Test content for add");
|
||||
builder.add("test_add_file.txt", "/");
|
||||
|
||||
// Verify the file was added
|
||||
let add_result = builder.run("cat /test_add_file.txt");
|
||||
assert_true(add_result.success, "Command should succeed");
|
||||
assert_true(add_result.stdout.contains("Test content for add"), "Added file content should match");
|
||||
print("✓ add(): File added successfully");
|
||||
|
||||
// Test copying a file
|
||||
print("Testing copy()...");
|
||||
// Create a test file
|
||||
file_write("test_copy_file.txt", "Test content for copy");
|
||||
builder.copy("test_copy_file.txt", "/");
|
||||
|
||||
// Verify the file was copied
|
||||
let copy_result = builder.run("cat /test_copy_file.txt");
|
||||
assert_true(copy_result.success, "Command should succeed");
|
||||
assert_true(copy_result.stdout.contains("Test content for copy"), "Copied file content should match");
|
||||
print("✓ copy(): File copied successfully");
|
||||
|
||||
// Test committing to an image
|
||||
print("Testing commit()...");
|
||||
let image_name = "rhai_test_image:latest";
|
||||
builder.commit(image_name);
|
||||
print("✓ commit(): Container committed to image successfully");
|
||||
|
||||
// Test removing the container
|
||||
print("Testing remove()...");
|
||||
builder.remove();
|
||||
print("✓ remove(): Container removed successfully");
|
||||
|
||||
// Clean up test files
|
||||
delete("test_add_file.txt");
|
||||
delete("test_copy_file.txt");
|
||||
|
||||
// Test image operations
|
||||
print("Testing image operations...");
|
||||
|
||||
// Test listing images
|
||||
print("Testing images()...");
|
||||
let images = builder.images();
|
||||
assert_true(images.len() > 0, "There should be at least one image");
|
||||
print("✓ images(): Images listed successfully");
|
||||
|
||||
// Test removing the image
|
||||
print("Testing image_remove()...");
|
||||
builder.image_remove(image_name);
|
||||
print("✓ image_remove(): Image removed successfully");
|
||||
|
||||
print("All Builder pattern tests completed successfully!");
|
||||
} catch(err) {
|
||||
print(`Error: ${err}`);
|
||||
|
||||
// Clean up in case of error
|
||||
try {
|
||||
// Remove test container if it exists
|
||||
run("buildah rm rhai_test_container");
|
||||
} catch(_) {}
|
||||
|
||||
try {
|
||||
// Remove test image if it exists
|
||||
run("buildah rmi rhai_test_image:latest");
|
||||
} catch(_) {}
|
||||
|
||||
try {
|
||||
// Remove test files if they exist
|
||||
delete("test_add_file.txt");
|
||||
delete("test_copy_file.txt");
|
||||
} catch(_) {}
|
||||
|
||||
throw err;
|
||||
}
|
@@ -1,150 +0,0 @@
|
||||
// 02_image_operations.rhai
|
||||
// Tests for Buildah image operations
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if buildah is available
|
||||
fn is_buildah_available() {
|
||||
try {
|
||||
let result = run("which buildah");
|
||||
return result.success;
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if an image exists
|
||||
fn image_exists(image_name) {
|
||||
try {
|
||||
let result = run(`buildah images -q ${image_name}`);
|
||||
return result.success && result.stdout.trim() != "";
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Buildah Image Operations ===");
|
||||
|
||||
// Check if buildah is available
|
||||
let buildah_available = is_buildah_available();
|
||||
if !buildah_available {
|
||||
print("Buildah is not available. Skipping Buildah tests.");
|
||||
// Exit gracefully without error
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ Buildah is available");
|
||||
|
||||
// Create a temporary directory for testing
|
||||
let test_dir = "rhai_test_buildah";
|
||||
mkdir(test_dir);
|
||||
|
||||
try {
|
||||
// Create a builder for testing
|
||||
let builder = bah_new("rhai_test_container", "alpine:latest");
|
||||
|
||||
// Enable debug mode
|
||||
builder.debug_mode = true;
|
||||
|
||||
// Test image_pull
|
||||
print("Testing image_pull()...");
|
||||
// Use a small image for testing
|
||||
let pull_result = builder.image_pull("alpine:3.14", true);
|
||||
assert_true(pull_result.success, "Image pull should succeed");
|
||||
print("✓ image_pull(): Image pulled successfully");
|
||||
|
||||
// Test image_tag
|
||||
print("Testing image_tag()...");
|
||||
let tag_result = builder.image_tag("alpine:3.14", "rhai_test_tag:latest");
|
||||
assert_true(tag_result.success, "Image tag should succeed");
|
||||
print("✓ image_tag(): Image tagged successfully");
|
||||
|
||||
// Test images (list)
|
||||
print("Testing images()...");
|
||||
let images = builder.images();
|
||||
assert_true(images.len() > 0, "There should be at least one image");
|
||||
|
||||
// Find our tagged image
|
||||
let found_tag = false;
|
||||
for image in images {
|
||||
if image.names.contains("rhai_test_tag:latest") {
|
||||
found_tag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert_true(found_tag, "Tagged image should be in the list");
|
||||
print("✓ images(): Images listed successfully");
|
||||
|
||||
// Test build
|
||||
print("Testing build()...");
|
||||
|
||||
// Create a simple Dockerfile
|
||||
let dockerfile_content = `FROM alpine:latest
|
||||
RUN echo "Hello from Dockerfile" > /hello.txt
|
||||
CMD ["cat", "/hello.txt"]
|
||||
`;
|
||||
file_write(`${test_dir}/Dockerfile`, dockerfile_content);
|
||||
|
||||
// Build the image
|
||||
let build_result = builder.build("rhai_test_build:latest", test_dir, "Dockerfile", "oci");
|
||||
assert_true(build_result.success, "Image build should succeed");
|
||||
print("✓ build(): Image built successfully");
|
||||
|
||||
// Verify the built image exists
|
||||
assert_true(image_exists("rhai_test_build:latest"), "Built image should exist");
|
||||
|
||||
// Test image_remove
|
||||
print("Testing image_remove()...");
|
||||
|
||||
// Remove the tagged image
|
||||
let remove_tag_result = builder.image_remove("rhai_test_tag:latest");
|
||||
assert_true(remove_tag_result.success, "Image removal should succeed");
|
||||
print("✓ image_remove(): Tagged image removed successfully");
|
||||
|
||||
// Remove the built image
|
||||
let remove_build_result = builder.image_remove("rhai_test_build:latest");
|
||||
assert_true(remove_build_result.success, "Image removal should succeed");
|
||||
print("✓ image_remove(): Built image removed successfully");
|
||||
|
||||
// Clean up
|
||||
builder.remove();
|
||||
print("✓ Cleanup: Container removed");
|
||||
|
||||
print("All image operations tests completed successfully!");
|
||||
} catch(err) {
|
||||
print(`Error: ${err}`);
|
||||
|
||||
// Clean up in case of error
|
||||
try {
|
||||
// Remove test container if it exists
|
||||
run("buildah rm rhai_test_container");
|
||||
} catch(_) {}
|
||||
|
||||
try {
|
||||
// Remove test images if they exist
|
||||
run("buildah rmi rhai_test_tag:latest");
|
||||
run("buildah rmi rhai_test_build:latest");
|
||||
} catch(_) {}
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
// Clean up test directory
|
||||
delete(test_dir);
|
||||
print("✓ Cleanup: Test directory removed");
|
||||
}
|
@@ -1,127 +0,0 @@
|
||||
// 03_container_operations.rhai
|
||||
// Tests for Buildah container operations
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if buildah is available
|
||||
fn is_buildah_available() {
|
||||
try {
|
||||
let result = run("which buildah");
|
||||
return result.success;
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Buildah Container Operations ===");
|
||||
|
||||
// Check if buildah is available
|
||||
let buildah_available = is_buildah_available();
|
||||
if !buildah_available {
|
||||
print("Buildah is not available. Skipping Buildah tests.");
|
||||
// Exit gracefully without error
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ Buildah is available");
|
||||
|
||||
try {
|
||||
// Test creating a new Builder
|
||||
print("Testing bah_new() and reset()...");
|
||||
let builder = bah_new("rhai_test_container", "alpine:latest");
|
||||
|
||||
// Enable debug mode
|
||||
builder.debug_mode = true;
|
||||
|
||||
// Test reset
|
||||
print("Testing reset()...");
|
||||
builder.reset();
|
||||
print("✓ reset(): Container reset successfully");
|
||||
|
||||
// Create a new container
|
||||
builder = bah_new("rhai_test_container", "alpine:latest");
|
||||
|
||||
// Test config
|
||||
print("Testing config()...");
|
||||
let config_options = #{
|
||||
"LABEL": "rhai_test=true",
|
||||
"ENV": "TEST_VAR=test_value"
|
||||
};
|
||||
builder.config(config_options);
|
||||
print("✓ config(): Container configured successfully");
|
||||
|
||||
// Test run with isolation
|
||||
print("Testing run_with_isolation()...");
|
||||
let isolation_result = builder.run_with_isolation("echo 'Hello with isolation'", "oci");
|
||||
assert_true(isolation_result.success, "Command with isolation should succeed");
|
||||
assert_true(isolation_result.stdout.contains("Hello with isolation"), "Command output should contain expected text");
|
||||
print("✓ run_with_isolation(): Command executed successfully");
|
||||
|
||||
// Test content operations
|
||||
print("Testing content operations...");
|
||||
|
||||
// Write content to a file
|
||||
let script_content = `#!/bin/sh
|
||||
echo "Hello from script"
|
||||
`;
|
||||
builder.write_content(script_content, "/script.sh");
|
||||
|
||||
// Make the script executable
|
||||
builder.run("chmod +x /script.sh");
|
||||
|
||||
// Run the script
|
||||
let script_result = builder.run("/script.sh");
|
||||
assert_true(script_result.success, "Script should execute successfully");
|
||||
assert_true(script_result.stdout.contains("Hello from script"), "Script output should contain expected text");
|
||||
print("✓ Content operations: Script created and executed successfully");
|
||||
|
||||
// Test commit with config
|
||||
print("Testing commit with config...");
|
||||
let commit_options = #{
|
||||
"author": "Rhai Test",
|
||||
"message": "Test commit"
|
||||
};
|
||||
builder.commit("rhai_test_commit:latest", commit_options);
|
||||
print("✓ commit(): Container committed with config successfully");
|
||||
|
||||
// Clean up
|
||||
builder.remove();
|
||||
print("✓ Cleanup: Container removed");
|
||||
|
||||
// Remove the committed image
|
||||
builder.image_remove("rhai_test_commit:latest");
|
||||
print("✓ Cleanup: Committed image removed");
|
||||
|
||||
print("All container operations tests completed successfully!");
|
||||
} catch(err) {
|
||||
print(`Error: ${err}`);
|
||||
|
||||
// Clean up in case of error
|
||||
try {
|
||||
// Remove test container if it exists
|
||||
run("buildah rm rhai_test_container");
|
||||
} catch(_) {}
|
||||
|
||||
try {
|
||||
// Remove test image if it exists
|
||||
run("buildah rmi rhai_test_commit:latest");
|
||||
} catch(_) {}
|
||||
|
||||
throw err;
|
||||
}
|
@@ -1,155 +0,0 @@
|
||||
// run_all_tests.rhai
|
||||
// Runs all Buildah module tests
|
||||
|
||||
print("=== Running Buildah Module Tests ===");
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if buildah is available
|
||||
fn is_buildah_available() {
|
||||
try {
|
||||
let result = run("which buildah");
|
||||
return result.success;
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Run each test directly
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
let skipped = 0;
|
||||
let total = 0;
|
||||
|
||||
// Check if buildah is available
|
||||
let buildah_available = is_buildah_available();
|
||||
if !buildah_available {
|
||||
print("Buildah is not available. Skipping all Buildah tests.");
|
||||
skipped = 3; // Skip all three tests
|
||||
total = 3;
|
||||
} else {
|
||||
// Test 1: Builder Pattern
|
||||
print("\n--- Running Builder Pattern Tests ---");
|
||||
try {
|
||||
// Create a builder
|
||||
let builder = bah_new("rhai_test_container", "alpine:latest");
|
||||
|
||||
// Test basic properties
|
||||
assert_true(builder.container_id != "", "Container ID should not be empty");
|
||||
assert_true(builder.name == "rhai_test_container", "Container name should match");
|
||||
|
||||
// Run a simple command
|
||||
let result = builder.run("echo 'Hello from container'");
|
||||
assert_true(result.success, "Command should succeed");
|
||||
|
||||
// Clean up
|
||||
builder.remove();
|
||||
|
||||
print("--- Builder Pattern Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Builder Pattern Tests: ${err}`);
|
||||
failed += 1;
|
||||
|
||||
// Clean up in case of error
|
||||
try {
|
||||
run("buildah rm rhai_test_container");
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
total += 1;
|
||||
|
||||
// Test 2: Image Operations
|
||||
print("\n--- Running Image Operations Tests ---");
|
||||
try {
|
||||
// Create a temporary directory for testing
|
||||
let test_dir = "rhai_test_buildah";
|
||||
mkdir(test_dir);
|
||||
|
||||
// Create a builder
|
||||
let builder = bah_new("rhai_test_container", "alpine:latest");
|
||||
|
||||
// List images
|
||||
let images = builder.images();
|
||||
assert_true(images.len() > 0, "There should be at least one image");
|
||||
|
||||
// Clean up
|
||||
builder.remove();
|
||||
delete(test_dir);
|
||||
|
||||
print("--- Image Operations Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Image Operations Tests: ${err}`);
|
||||
failed += 1;
|
||||
|
||||
// Clean up in case of error
|
||||
try {
|
||||
run("buildah rm rhai_test_container");
|
||||
delete("rhai_test_buildah");
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
total += 1;
|
||||
|
||||
// Test 3: Container Operations
|
||||
print("\n--- Running Container Operations Tests ---");
|
||||
try {
|
||||
// Create a builder
|
||||
let builder = bah_new("rhai_test_container", "alpine:latest");
|
||||
|
||||
// Test reset
|
||||
builder.reset();
|
||||
|
||||
// Create a new container
|
||||
builder = bah_new("rhai_test_container", "alpine:latest");
|
||||
|
||||
// Run a command
|
||||
let result = builder.run("echo 'Hello from container'");
|
||||
assert_true(result.success, "Command should succeed");
|
||||
|
||||
// Clean up
|
||||
builder.remove();
|
||||
|
||||
print("--- Container Operations Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Container Operations Tests: ${err}`);
|
||||
failed += 1;
|
||||
|
||||
// Clean up in case of error
|
||||
try {
|
||||
run("buildah rm rhai_test_container");
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
total += 1;
|
||||
}
|
||||
|
||||
print("\n=== Test Summary ===");
|
||||
print(`Passed: ${passed}`);
|
||||
print(`Failed: ${failed}`);
|
||||
print(`Skipped: ${skipped}`);
|
||||
print(`Total: ${total}`);
|
||||
|
||||
if failed == 0 {
|
||||
if skipped > 0 {
|
||||
print("\n⚠️ All tests skipped or passed!");
|
||||
} else {
|
||||
print("\n✅ All tests passed!");
|
||||
}
|
||||
} else {
|
||||
print("\n❌ Some tests failed!");
|
||||
}
|
||||
|
||||
// Return the number of failed tests (0 means success)
|
||||
failed;
|
@@ -1,76 +0,0 @@
|
||||
// 01_git_basic.rhai
|
||||
// Tests for basic Git operations in the Git module
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a temporary directory for Git operations
|
||||
let test_dir = "rhai_test_git";
|
||||
mkdir(test_dir);
|
||||
print(`Created test directory: ${test_dir}`);
|
||||
|
||||
// Test GitTree constructor
|
||||
print("Testing GitTree constructor...");
|
||||
let git_tree = git_tree_new(test_dir);
|
||||
print("✓ GitTree created successfully");
|
||||
|
||||
// Test GitTree.list() with empty directory
|
||||
print("Testing GitTree.list() with empty directory...");
|
||||
let repos = git_tree.list();
|
||||
assert_true(repos.len() == 0, "Expected empty list of repositories");
|
||||
print(`✓ GitTree.list(): Found ${repos.len()} repositories (expected 0)`);
|
||||
|
||||
// Test GitTree.find() with empty directory
|
||||
print("Testing GitTree.find() with empty directory...");
|
||||
let found_repos = git_tree.find("*");
|
||||
assert_true(found_repos.len() == 0, "Expected empty list of repositories");
|
||||
print(`✓ GitTree.find(): Found ${found_repos.len()} repositories (expected 0)`);
|
||||
|
||||
// Test GitTree.get() with a URL to clone a repository
|
||||
// We'll use a small, public repository for testing
|
||||
print("Testing GitTree.get() with URL...");
|
||||
let repo_url = "https://github.com/rhaiscript/playground.git";
|
||||
let repo = git_tree.get(repo_url);
|
||||
print(`✓ GitTree.get(): Repository cloned successfully to ${repo.path()}`);
|
||||
|
||||
// Test GitRepo.path()
|
||||
print("Testing GitRepo.path()...");
|
||||
let repo_path = repo.path();
|
||||
assert_true(repo_path.contains(test_dir), "Repository path should contain test directory");
|
||||
print(`✓ GitRepo.path(): ${repo_path}`);
|
||||
|
||||
// Test GitRepo.has_changes()
|
||||
print("Testing GitRepo.has_changes()...");
|
||||
let has_changes = repo.has_changes();
|
||||
print(`✓ GitRepo.has_changes(): ${has_changes}`);
|
||||
|
||||
// Test GitTree.list() after cloning
|
||||
print("Testing GitTree.list() after cloning...");
|
||||
let repos_after_clone = git_tree.list();
|
||||
assert_true(repos_after_clone.len() > 0, "Expected non-empty list of repositories");
|
||||
print(`✓ GitTree.list(): Found ${repos_after_clone.len()} repositories`);
|
||||
|
||||
// Test GitTree.find() after cloning
|
||||
print("Testing GitTree.find() after cloning...");
|
||||
let found_repos_after_clone = git_tree.find("*");
|
||||
assert_true(found_repos_after_clone.len() > 0, "Expected non-empty list of repositories");
|
||||
print(`✓ GitTree.find(): Found ${found_repos_after_clone.len()} repositories`);
|
||||
|
||||
// Test GitTree.get() with a path to an existing repository
|
||||
print("Testing GitTree.get() with path...");
|
||||
let repo_name = repos_after_clone[0];
|
||||
let repo_by_path = git_tree.get(repo_name);
|
||||
print(`✓ GitTree.get(): Repository opened successfully from ${repo_by_path.path()}`);
|
||||
|
||||
// Clean up
|
||||
print("Cleaning up...");
|
||||
delete(test_dir);
|
||||
assert_true(!exist(test_dir), "Directory deletion failed");
|
||||
print(`✓ Cleanup: Directory ${test_dir} removed`);
|
||||
|
||||
print("All basic Git tests completed successfully!");
|
@@ -1,63 +0,0 @@
|
||||
// 02_git_operations.rhai
|
||||
// Tests for Git operations like pull, reset, commit, and push
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a temporary directory for Git operations
|
||||
let test_dir = "rhai_test_git_ops";
|
||||
mkdir(test_dir);
|
||||
print(`Created test directory: ${test_dir}`);
|
||||
|
||||
// Create a GitTree
|
||||
print("Creating GitTree...");
|
||||
let git_tree = git_tree_new(test_dir);
|
||||
print("✓ GitTree created successfully");
|
||||
|
||||
// Clone a repository
|
||||
print("Cloning repository...");
|
||||
let repo_url = "https://github.com/rhaiscript/playground.git";
|
||||
let repo = git_tree.get(repo_url);
|
||||
print(`✓ Repository cloned successfully to ${repo.path()}`);
|
||||
|
||||
// Test GitRepo.pull()
|
||||
print("Testing GitRepo.pull()...");
|
||||
try {
|
||||
let pull_result = repo.pull();
|
||||
print("✓ GitRepo.pull(): Pull successful");
|
||||
} catch(err) {
|
||||
// Pull might fail if there are local changes or network issues
|
||||
// This is expected in some cases, so we'll just log it
|
||||
print(`Note: Pull failed with error: ${err}`);
|
||||
print("✓ GitRepo.pull(): Error handled gracefully");
|
||||
}
|
||||
|
||||
// Test GitRepo.reset()
|
||||
print("Testing GitRepo.reset()...");
|
||||
try {
|
||||
let reset_result = repo.reset();
|
||||
print("✓ GitRepo.reset(): Reset successful");
|
||||
} catch(err) {
|
||||
// Reset might fail in some cases
|
||||
print(`Note: Reset failed with error: ${err}`);
|
||||
print("✓ GitRepo.reset(): Error handled gracefully");
|
||||
}
|
||||
|
||||
// Note: We won't test commit and push as they would modify the remote repository
|
||||
// Instead, we'll just verify that the methods exist and can be called
|
||||
|
||||
print("Note: Not testing commit and push to avoid modifying remote repositories");
|
||||
print("✓ GitRepo.commit() and GitRepo.push() methods exist");
|
||||
|
||||
// Clean up
|
||||
print("Cleaning up...");
|
||||
delete(test_dir);
|
||||
assert_true(!exist(test_dir), "Directory deletion failed");
|
||||
print(`✓ Cleanup: Directory ${test_dir} removed`);
|
||||
|
||||
print("All Git operations tests completed successfully!");
|
@@ -1,94 +0,0 @@
|
||||
// run_all_tests.rhai
|
||||
// Runs all Git module tests
|
||||
|
||||
print("=== Running Git Module Tests ===");
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Run each test directly
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
// Test 1: Basic Git Operations
|
||||
print("\n--- Running Basic Git Operations Tests ---");
|
||||
try {
|
||||
// Create a temporary directory for Git operations
|
||||
let test_dir = "rhai_test_git";
|
||||
mkdir(test_dir);
|
||||
print(`Created test directory: ${test_dir}`);
|
||||
|
||||
// Test GitTree constructor
|
||||
print("Testing GitTree constructor...");
|
||||
let git_tree = git_tree_new(test_dir);
|
||||
print("✓ GitTree created successfully");
|
||||
|
||||
// Test GitTree.list() with empty directory
|
||||
print("Testing GitTree.list() with empty directory...");
|
||||
let repos = git_tree.list();
|
||||
assert_true(repos.len() == 0, "Expected empty list of repositories");
|
||||
print(`✓ GitTree.list(): Found ${repos.len()} repositories (expected 0)`);
|
||||
|
||||
// Test GitTree.find() with empty directory
|
||||
print("Testing GitTree.find() with empty directory...");
|
||||
let found_repos = git_tree.find("*");
|
||||
assert_true(found_repos.len() == 0, "Expected empty list of repositories");
|
||||
print(`✓ GitTree.find(): Found ${found_repos.len()} repositories (expected 0)`);
|
||||
|
||||
// Clean up
|
||||
print("Cleaning up...");
|
||||
delete(test_dir);
|
||||
assert_true(!exist(test_dir), "Directory deletion failed");
|
||||
print(`✓ Cleanup: Directory ${test_dir} removed`);
|
||||
|
||||
print("--- Basic Git Operations Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Basic Git Operations Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
|
||||
// Test 2: Git Repository Operations
|
||||
print("\n--- Running Git Repository Operations Tests ---");
|
||||
try {
|
||||
// Create a temporary directory for Git operations
|
||||
let test_dir = "rhai_test_git_ops";
|
||||
mkdir(test_dir);
|
||||
print(`Created test directory: ${test_dir}`);
|
||||
|
||||
// Create a GitTree
|
||||
print("Creating GitTree...");
|
||||
let git_tree = git_tree_new(test_dir);
|
||||
print("✓ GitTree created successfully");
|
||||
|
||||
// Clean up
|
||||
print("Cleaning up...");
|
||||
delete(test_dir);
|
||||
assert_true(!exist(test_dir), "Directory deletion failed");
|
||||
print(`✓ Cleanup: Directory ${test_dir} removed`);
|
||||
|
||||
print("--- Git Repository Operations Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Git Repository Operations Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
|
||||
print("\n=== Test Summary ===");
|
||||
print(`Passed: ${passed}`);
|
||||
print(`Failed: ${failed}`);
|
||||
print(`Total: ${passed + failed}`);
|
||||
|
||||
if failed == 0 {
|
||||
print("\n✅ All tests passed!");
|
||||
} else {
|
||||
print("\n❌ Some tests failed!");
|
||||
}
|
||||
|
||||
// Return the number of failed tests (0 means success)
|
||||
failed;
|
@@ -1,176 +0,0 @@
|
||||
// 01_container_operations.rhai
|
||||
// Tests for Nerdctl container operations
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if nerdctl is available
|
||||
fn is_nerdctl_available() {
|
||||
try {
|
||||
let result = run("which nerdctl");
|
||||
return result.success;
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if a container exists
|
||||
fn container_exists(container_name) {
|
||||
try {
|
||||
let result = run(`nerdctl ps -a --format "{{.Names}}" | grep -w ${container_name}`);
|
||||
return result.success;
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to clean up a container if it exists
|
||||
fn cleanup_container(container_name) {
|
||||
if container_exists(container_name) {
|
||||
try {
|
||||
run(`nerdctl stop ${container_name}`);
|
||||
run(`nerdctl rm ${container_name}`);
|
||||
print(`Cleaned up container: ${container_name}`);
|
||||
} catch(err) {
|
||||
print(`Error cleaning up container ${container_name}: ${err}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Nerdctl Container Operations ===");
|
||||
|
||||
// Check if nerdctl is available
|
||||
let nerdctl_available = is_nerdctl_available();
|
||||
if !nerdctl_available {
|
||||
print("nerdctl is not available. Skipping Nerdctl tests.");
|
||||
// Exit gracefully without error
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ nerdctl is available");
|
||||
|
||||
// Define test container name
|
||||
let container_name = "rhai_test_container";
|
||||
|
||||
// Clean up any existing test container
|
||||
cleanup_container(container_name);
|
||||
|
||||
try {
|
||||
// Test creating a new Container
|
||||
print("Testing nerdctl_container_new()...");
|
||||
let container = nerdctl_container_new(container_name);
|
||||
|
||||
// Test Container properties
|
||||
print("Testing Container properties...");
|
||||
assert_eq(container.name, container_name, "Container name should match");
|
||||
assert_eq(container.container_id, "", "Container ID should be empty initially");
|
||||
|
||||
// Test setting container image
|
||||
print("Testing with_image()...");
|
||||
container.with_image("alpine:latest");
|
||||
assert_eq(container.image, "alpine:latest", "Container image should match");
|
||||
|
||||
// Test setting detach mode
|
||||
print("Testing with_detach()...");
|
||||
container.with_detach(true);
|
||||
assert_true(container.detach, "Container detach mode should be true");
|
||||
|
||||
// Test setting environment variables
|
||||
print("Testing with_env()...");
|
||||
container.with_env("TEST_VAR", "test_value");
|
||||
|
||||
// Test setting multiple environment variables
|
||||
print("Testing with_envs()...");
|
||||
let env_map = #{
|
||||
"VAR1": "value1",
|
||||
"VAR2": "value2"
|
||||
};
|
||||
container.with_envs(env_map);
|
||||
|
||||
// Test setting ports
|
||||
print("Testing with_port()...");
|
||||
container.with_port("8080:80");
|
||||
|
||||
// Test setting multiple ports
|
||||
print("Testing with_ports()...");
|
||||
container.with_ports(["9090:90", "7070:70"]);
|
||||
|
||||
// Test setting volumes
|
||||
print("Testing with_volume()...");
|
||||
// Create a test directory for volume mounting
|
||||
let test_dir = "rhai_test_nerdctl_volume";
|
||||
mkdir(test_dir);
|
||||
container.with_volume(`${test_dir}:/data`);
|
||||
|
||||
// Test setting resource limits
|
||||
print("Testing with_cpu_limit() and with_memory_limit()...");
|
||||
container.with_cpu_limit("0.5");
|
||||
container.with_memory_limit("256m");
|
||||
|
||||
// Test running the container
|
||||
print("Testing run()...");
|
||||
let run_result = container.run();
|
||||
assert_true(run_result.success, "Container run should succeed");
|
||||
assert_true(container.container_id != "", "Container ID should not be empty after run");
|
||||
print(`✓ run(): Container started with ID: ${container.container_id}`);
|
||||
|
||||
// Test executing a command in the container
|
||||
print("Testing exec()...");
|
||||
let exec_result = container.exec("echo 'Hello from container'");
|
||||
assert_true(exec_result.success, "Container exec should succeed");
|
||||
assert_true(exec_result.stdout.contains("Hello from container"), "Exec output should contain expected text");
|
||||
print("✓ exec(): Command executed successfully");
|
||||
|
||||
// Test getting container logs
|
||||
print("Testing logs()...");
|
||||
let logs_result = container.logs();
|
||||
assert_true(logs_result.success, "Container logs should succeed");
|
||||
print("✓ logs(): Logs retrieved successfully");
|
||||
|
||||
// Test stopping the container
|
||||
print("Testing stop()...");
|
||||
let stop_result = container.stop();
|
||||
assert_true(stop_result.success, "Container stop should succeed");
|
||||
print("✓ stop(): Container stopped successfully");
|
||||
|
||||
// Test removing the container
|
||||
print("Testing remove()...");
|
||||
let remove_result = container.remove();
|
||||
assert_true(remove_result.success, "Container remove should succeed");
|
||||
print("✓ remove(): Container removed successfully");
|
||||
|
||||
// Clean up test directory
|
||||
delete(test_dir);
|
||||
print("✓ Cleanup: Test directory removed");
|
||||
|
||||
print("All container operations tests completed successfully!");
|
||||
} catch(err) {
|
||||
print(`Error: ${err}`);
|
||||
|
||||
// Clean up in case of error
|
||||
cleanup_container(container_name);
|
||||
|
||||
// Clean up test directory
|
||||
try {
|
||||
delete("rhai_test_nerdctl_volume");
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
@@ -1,168 +0,0 @@
|
||||
// 02_image_operations.rhai
|
||||
// Tests for Nerdctl image operations
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if nerdctl is available
|
||||
fn is_nerdctl_available() {
|
||||
try {
|
||||
let result = run("which nerdctl");
|
||||
return result.success;
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if an image exists
|
||||
fn image_exists(image_name) {
|
||||
try {
|
||||
let result = run(`nerdctl images -q ${image_name}`);
|
||||
return result.success && result.stdout.trim() != "";
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to clean up an image if it exists
|
||||
fn cleanup_image(image_name) {
|
||||
if image_exists(image_name) {
|
||||
try {
|
||||
run(`nerdctl rmi ${image_name}`);
|
||||
print(`Cleaned up image: ${image_name}`);
|
||||
} catch(err) {
|
||||
print(`Error cleaning up image ${image_name}: ${err}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Nerdctl Image Operations ===");
|
||||
|
||||
// Check if nerdctl is available
|
||||
let nerdctl_available = is_nerdctl_available();
|
||||
if !nerdctl_available {
|
||||
print("nerdctl is not available. Skipping Nerdctl tests.");
|
||||
// Exit gracefully without error
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ nerdctl is available");
|
||||
|
||||
// Create a temporary directory for testing
|
||||
let test_dir = "rhai_test_nerdctl";
|
||||
mkdir(test_dir);
|
||||
|
||||
try {
|
||||
// Test pulling an image
|
||||
print("Testing nerdctl_image_pull()...");
|
||||
// Use a small image for testing
|
||||
let pull_result = nerdctl_image_pull("alpine:latest");
|
||||
assert_true(pull_result.success, "Image pull should succeed");
|
||||
print("✓ nerdctl_image_pull(): Image pulled successfully");
|
||||
|
||||
// Test listing images
|
||||
print("Testing nerdctl_images()...");
|
||||
let images_result = nerdctl_images();
|
||||
assert_true(images_result.success, "Image listing should succeed");
|
||||
assert_true(images_result.stdout.contains("alpine"), "Image list should contain alpine");
|
||||
print("✓ nerdctl_images(): Images listed successfully");
|
||||
|
||||
// Test tagging an image
|
||||
print("Testing nerdctl_image_tag()...");
|
||||
let tag_result = nerdctl_image_tag("alpine:latest", "rhai_test_image:latest");
|
||||
assert_true(tag_result.success, "Image tag should succeed");
|
||||
print("✓ nerdctl_image_tag(): Image tagged successfully");
|
||||
|
||||
// Test building an image
|
||||
print("Testing nerdctl_image_build()...");
|
||||
|
||||
// Create a simple Dockerfile
|
||||
let dockerfile_content = `FROM alpine:latest
|
||||
RUN echo "Hello from Dockerfile" > /hello.txt
|
||||
CMD ["cat", "/hello.txt"]
|
||||
`;
|
||||
file_write(`${test_dir}/Dockerfile`, dockerfile_content);
|
||||
|
||||
// Build the image
|
||||
let build_result = nerdctl_image_build("rhai_test_build:latest", test_dir);
|
||||
assert_true(build_result.success, "Image build should succeed");
|
||||
print("✓ nerdctl_image_build(): Image built successfully");
|
||||
|
||||
// Test running a container from the built image
|
||||
print("Testing container from built image...");
|
||||
let container_name = "rhai_test_container_from_build";
|
||||
|
||||
// Clean up any existing container with the same name
|
||||
try {
|
||||
run(`nerdctl stop ${container_name}`);
|
||||
run(`nerdctl rm ${container_name}`);
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
|
||||
// Run the container
|
||||
let run_result = nerdctl_run_with_name("rhai_test_build:latest", container_name);
|
||||
assert_true(run_result.success, "Container run should succeed");
|
||||
assert_true(run_result.stdout.contains("Hello from Dockerfile"), "Container output should contain expected text");
|
||||
print("✓ Container from built image ran successfully");
|
||||
|
||||
// Clean up the container
|
||||
let stop_result = nerdctl_stop(container_name);
|
||||
assert_true(stop_result.success, "Container stop should succeed");
|
||||
let remove_result = nerdctl_remove(container_name);
|
||||
assert_true(remove_result.success, "Container remove should succeed");
|
||||
print("✓ Cleanup: Container removed");
|
||||
|
||||
// Test removing images
|
||||
print("Testing nerdctl_image_remove()...");
|
||||
|
||||
// Remove the tagged image
|
||||
let remove_tag_result = nerdctl_image_remove("rhai_test_image:latest");
|
||||
assert_true(remove_tag_result.success, "Image removal should succeed");
|
||||
print("✓ nerdctl_image_remove(): Tagged image removed successfully");
|
||||
|
||||
// Remove the built image
|
||||
let remove_build_result = nerdctl_image_remove("rhai_test_build:latest");
|
||||
assert_true(remove_build_result.success, "Image removal should succeed");
|
||||
print("✓ nerdctl_image_remove(): Built image removed successfully");
|
||||
|
||||
print("All image operations tests completed successfully!");
|
||||
} catch(err) {
|
||||
print(`Error: ${err}`);
|
||||
|
||||
// Clean up in case of error
|
||||
try {
|
||||
run("nerdctl stop rhai_test_container_from_build");
|
||||
run("nerdctl rm rhai_test_container_from_build");
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
|
||||
try {
|
||||
cleanup_image("rhai_test_image:latest");
|
||||
cleanup_image("rhai_test_build:latest");
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
// Clean up test directory
|
||||
delete(test_dir);
|
||||
print("✓ Cleanup: Test directory removed");
|
||||
}
|
@@ -1,166 +0,0 @@
|
||||
// 03_container_builder.rhai
|
||||
// Tests for Nerdctl Container Builder pattern
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if nerdctl is available
|
||||
fn is_nerdctl_available() {
|
||||
try {
|
||||
let result = run("which nerdctl");
|
||||
return result.success;
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if a container exists
|
||||
fn container_exists(container_name) {
|
||||
try {
|
||||
let result = run(`nerdctl ps -a --format "{{.Names}}" | grep -w ${container_name}`);
|
||||
return result.success;
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to clean up a container if it exists
|
||||
fn cleanup_container(container_name) {
|
||||
if container_exists(container_name) {
|
||||
try {
|
||||
run(`nerdctl stop ${container_name}`);
|
||||
run(`nerdctl rm ${container_name}`);
|
||||
print(`Cleaned up container: ${container_name}`);
|
||||
} catch(err) {
|
||||
print(`Error cleaning up container ${container_name}: ${err}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Nerdctl Container Builder Pattern ===");
|
||||
|
||||
// Check if nerdctl is available
|
||||
let nerdctl_available = is_nerdctl_available();
|
||||
if !nerdctl_available {
|
||||
print("nerdctl is not available. Skipping Nerdctl tests.");
|
||||
// Exit gracefully without error
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ nerdctl is available");
|
||||
|
||||
// Define test container name
|
||||
let container_name = "rhai_test_builder";
|
||||
|
||||
// Clean up any existing test container
|
||||
cleanup_container(container_name);
|
||||
|
||||
// Create test directories
|
||||
let work_dir = "rhai_test_nerdctl_work";
|
||||
let config_dir = "rhai_test_nerdctl_config";
|
||||
mkdir(work_dir);
|
||||
mkdir(config_dir);
|
||||
|
||||
try {
|
||||
// Test creating a container from an image with builder pattern
|
||||
print("Testing nerdctl_container_from_image() with builder pattern...");
|
||||
|
||||
// Create a container with a rich set of options using the builder pattern
|
||||
let container = nerdctl_container_from_image(container_name, "alpine:latest")
|
||||
.reset() // Reset to default configuration
|
||||
.with_detach(true)
|
||||
.with_ports(["8080:80", "9090:90"])
|
||||
.with_volumes([`${work_dir}:/data`, `${config_dir}:/config`])
|
||||
.with_envs(#{
|
||||
"ENV1": "value1",
|
||||
"ENV2": "value2",
|
||||
"TEST_MODE": "true"
|
||||
})
|
||||
.with_network("bridge")
|
||||
.with_cpu_limit("0.5")
|
||||
.with_memory_limit("256m");
|
||||
|
||||
// Verify container properties
|
||||
assert_eq(container.name, container_name, "Container name should match");
|
||||
assert_eq(container.image, "alpine:latest", "Container image should match");
|
||||
assert_true(container.detach, "Container detach mode should be true");
|
||||
|
||||
// Run the container
|
||||
print("Testing run() with builder pattern...");
|
||||
let run_result = container.run();
|
||||
assert_true(run_result.success, "Container run should succeed");
|
||||
assert_true(container.container_id != "", "Container ID should not be empty after run");
|
||||
print(`✓ run(): Container started with ID: ${container.container_id}`);
|
||||
|
||||
// Test environment variables
|
||||
print("Testing environment variables...");
|
||||
let env_result = container.exec("env");
|
||||
assert_true(env_result.success, "Container exec should succeed");
|
||||
assert_true(env_result.stdout.contains("ENV1=value1"), "Environment variable ENV1 should be set");
|
||||
assert_true(env_result.stdout.contains("ENV2=value2"), "Environment variable ENV2 should be set");
|
||||
assert_true(env_result.stdout.contains("TEST_MODE=true"), "Environment variable TEST_MODE should be set");
|
||||
print("✓ Environment variables set correctly");
|
||||
|
||||
// Test volume mounts
|
||||
print("Testing volume mounts...");
|
||||
|
||||
// Create a test file in the work directory
|
||||
file_write(`${work_dir}/test.txt`, "Hello from host");
|
||||
|
||||
// Check if the file is accessible in the container
|
||||
let volume_result = container.exec("cat /data/test.txt");
|
||||
assert_true(volume_result.success, "Container exec should succeed");
|
||||
assert_true(volume_result.stdout.contains("Hello from host"), "Volume mount should work correctly");
|
||||
print("✓ Volume mounts working correctly");
|
||||
|
||||
// Test writing from container to volume
|
||||
print("Testing writing from container to volume...");
|
||||
let write_result = container.exec("echo 'Hello from container' > /config/container.txt");
|
||||
assert_true(write_result.success, "Container exec should succeed");
|
||||
|
||||
// Check if the file was created on the host
|
||||
let host_file_content = file_read(`${config_dir}/container.txt`);
|
||||
assert_true(host_file_content.contains("Hello from container"), "Container should be able to write to volume");
|
||||
print("✓ Container can write to volume");
|
||||
|
||||
// Test stopping the container
|
||||
print("Testing stop()...");
|
||||
let stop_result = container.stop();
|
||||
assert_true(stop_result.success, "Container stop should succeed");
|
||||
print("✓ stop(): Container stopped successfully");
|
||||
|
||||
// Test removing the container
|
||||
print("Testing remove()...");
|
||||
let remove_result = container.remove();
|
||||
assert_true(remove_result.success, "Container remove should succeed");
|
||||
print("✓ remove(): Container removed successfully");
|
||||
|
||||
print("All container builder pattern tests completed successfully!");
|
||||
} catch(err) {
|
||||
print(`Error: ${err}`);
|
||||
|
||||
// Clean up in case of error
|
||||
cleanup_container(container_name);
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
// Clean up test directories
|
||||
delete(work_dir);
|
||||
delete(config_dir);
|
||||
print("✓ Cleanup: Test directories removed");
|
||||
}
|
@@ -1,183 +0,0 @@
|
||||
// run_all_tests.rhai
|
||||
// Runs all Nerdctl module tests
|
||||
|
||||
print("=== Running Nerdctl Module Tests ===");
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if nerdctl is available
|
||||
fn is_nerdctl_available() {
|
||||
try {
|
||||
let result = run("which nerdctl");
|
||||
return result.success;
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to clean up a container if it exists
|
||||
fn cleanup_container(container_name) {
|
||||
try {
|
||||
run(`nerdctl stop ${container_name}`);
|
||||
run(`nerdctl rm ${container_name}`);
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
|
||||
// Run each test directly
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
let skipped = 0;
|
||||
let total = 0;
|
||||
|
||||
// Check if nerdctl is available
|
||||
let nerdctl_available = is_nerdctl_available();
|
||||
if !nerdctl_available {
|
||||
print("nerdctl is not available. Skipping all Nerdctl tests.");
|
||||
skipped = 3; // Skip all three tests
|
||||
total = 3;
|
||||
} else {
|
||||
// Test 1: Container Operations
|
||||
print("\n--- Running Container Operations Tests ---");
|
||||
try {
|
||||
// Define test container name
|
||||
let container_name = "rhai_test_container";
|
||||
|
||||
// Clean up any existing test container
|
||||
cleanup_container(container_name);
|
||||
|
||||
// Create a new Container
|
||||
let container = nerdctl_container_new(container_name);
|
||||
|
||||
// Set container image
|
||||
container.with_image("alpine:latest");
|
||||
|
||||
// Set detach mode
|
||||
container.with_detach(true);
|
||||
|
||||
// Run the container
|
||||
let run_result = container.run();
|
||||
assert_true(run_result.success, "Container run should succeed");
|
||||
|
||||
// Execute a command in the container
|
||||
let exec_result = container.exec("echo 'Hello from container'");
|
||||
assert_true(exec_result.success, "Container exec should succeed");
|
||||
|
||||
// Clean up
|
||||
container.stop();
|
||||
container.remove();
|
||||
|
||||
print("--- Container Operations Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Container Operations Tests: ${err}`);
|
||||
failed += 1;
|
||||
|
||||
// Clean up in case of error
|
||||
cleanup_container("rhai_test_container");
|
||||
}
|
||||
total += 1;
|
||||
|
||||
// Test 2: Image Operations
|
||||
print("\n--- Running Image Operations Tests ---");
|
||||
try {
|
||||
// Create a temporary directory for testing
|
||||
let test_dir = "rhai_test_nerdctl";
|
||||
mkdir(test_dir);
|
||||
|
||||
// Pull a small image for testing
|
||||
let pull_result = nerdctl_image_pull("alpine:latest");
|
||||
assert_true(pull_result.success, "Image pull should succeed");
|
||||
|
||||
// List images
|
||||
let images_result = nerdctl_images();
|
||||
assert_true(images_result.success, "Image listing should succeed");
|
||||
|
||||
// Clean up
|
||||
delete(test_dir);
|
||||
|
||||
print("--- Image Operations Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Image Operations Tests: ${err}`);
|
||||
failed += 1;
|
||||
|
||||
// Clean up in case of error
|
||||
try {
|
||||
delete("rhai_test_nerdctl");
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
total += 1;
|
||||
|
||||
// Test 3: Container Builder Pattern
|
||||
print("\n--- Running Container Builder Pattern Tests ---");
|
||||
try {
|
||||
// Define test container name
|
||||
let container_name = "rhai_test_builder";
|
||||
|
||||
// Clean up any existing test container
|
||||
cleanup_container(container_name);
|
||||
|
||||
// Create test directory
|
||||
let work_dir = "rhai_test_nerdctl_work";
|
||||
mkdir(work_dir);
|
||||
|
||||
// Create a container with builder pattern
|
||||
let container = nerdctl_container_from_image(container_name, "alpine:latest")
|
||||
.reset()
|
||||
.with_detach(true)
|
||||
.with_volumes([`${work_dir}:/data`]);
|
||||
|
||||
// Run the container
|
||||
let run_result = container.run();
|
||||
assert_true(run_result.success, "Container run should succeed");
|
||||
|
||||
// Clean up
|
||||
container.stop();
|
||||
container.remove();
|
||||
delete(work_dir);
|
||||
|
||||
print("--- Container Builder Pattern Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Container Builder Pattern Tests: ${err}`);
|
||||
failed += 1;
|
||||
|
||||
// Clean up in case of error
|
||||
cleanup_container("rhai_test_builder");
|
||||
try {
|
||||
delete("rhai_test_nerdctl_work");
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
total += 1;
|
||||
}
|
||||
|
||||
print("\n=== Test Summary ===");
|
||||
print(`Passed: ${passed}`);
|
||||
print(`Failed: ${failed}`);
|
||||
print(`Skipped: ${skipped}`);
|
||||
print(`Total: ${total}`);
|
||||
|
||||
if failed == 0 {
|
||||
if skipped > 0 {
|
||||
print("\n⚠️ All tests skipped or passed!");
|
||||
} else {
|
||||
print("\n✅ All tests passed!");
|
||||
}
|
||||
} else {
|
||||
print("\n❌ Some tests failed!");
|
||||
}
|
||||
|
||||
// Return the number of failed tests (0 means success)
|
||||
failed;
|
@@ -1,111 +0,0 @@
|
||||
// 01_file_operations.rhai
|
||||
// Tests for file system operations in the OS module
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a test directory structure
|
||||
let test_dir = "rhai_test_fs";
|
||||
let sub_dir = test_dir + "/subdir";
|
||||
|
||||
// Test mkdir function
|
||||
print("Testing mkdir...");
|
||||
let mkdir_result = mkdir(test_dir);
|
||||
assert_true(exist(test_dir), "Directory creation failed");
|
||||
print(`✓ mkdir: ${mkdir_result}`);
|
||||
|
||||
// Test nested directory creation
|
||||
let nested_result = mkdir(sub_dir);
|
||||
assert_true(exist(sub_dir), "Nested directory creation failed");
|
||||
print(`✓ mkdir (nested): ${nested_result}`);
|
||||
|
||||
// Test file_write function
|
||||
let test_file = test_dir + "/test.txt";
|
||||
let file_content = "This is a test file created by Rhai test script.";
|
||||
let write_result = file_write(test_file, file_content);
|
||||
assert_true(exist(test_file), "File creation failed");
|
||||
print(`✓ file_write: ${write_result}`);
|
||||
|
||||
// Test file_read function
|
||||
let read_content = file_read(test_file);
|
||||
assert_true(read_content == file_content, "File content doesn't match");
|
||||
print(`✓ file_read: Content matches`);
|
||||
|
||||
// Test file_size function
|
||||
let size = file_size(test_file);
|
||||
assert_true(size > 0, "File size should be greater than 0");
|
||||
print(`✓ file_size: ${size} bytes`);
|
||||
|
||||
// Test file_write_append function
|
||||
let append_content = "\nThis is appended content.";
|
||||
let append_result = file_write_append(test_file, append_content);
|
||||
let new_content = file_read(test_file);
|
||||
assert_true(new_content == file_content + append_content, "Appended content doesn't match");
|
||||
print(`✓ file_write_append: ${append_result}`);
|
||||
|
||||
// Test copy function
|
||||
let copied_file = test_dir + "/copied.txt";
|
||||
let copy_result = copy(test_file, copied_file);
|
||||
assert_true(exist(copied_file), "File copy failed");
|
||||
print(`✓ copy: ${copy_result}`);
|
||||
|
||||
// Test mv function
|
||||
let moved_file = test_dir + "/moved.txt";
|
||||
let mv_result = mv(copied_file, moved_file);
|
||||
assert_true(exist(moved_file), "File move failed");
|
||||
assert_true(!exist(copied_file), "Source file still exists after move");
|
||||
print(`✓ mv: ${mv_result}`);
|
||||
|
||||
// Test find_file function
|
||||
let found_file = find_file(test_dir, "*.txt");
|
||||
assert_true(found_file.contains("test.txt") || found_file.contains("moved.txt"), "find_file failed");
|
||||
print(`✓ find_file: ${found_file}`);
|
||||
|
||||
// Test find_files function
|
||||
let found_files = find_files(test_dir, "*.txt");
|
||||
assert_true(found_files.len() == 2, "find_files should find 2 files");
|
||||
print(`✓ find_files: Found ${found_files.len()} files`);
|
||||
|
||||
// Test find_dir function
|
||||
let found_dir = find_dir(test_dir, "sub*");
|
||||
assert_true(found_dir.contains("subdir"), "find_dir failed");
|
||||
print(`✓ find_dir: ${found_dir}`);
|
||||
|
||||
// Test find_dirs function
|
||||
let found_dirs = find_dirs(test_dir, "sub*");
|
||||
assert_true(found_dirs.len() == 1, "find_dirs should find 1 directory");
|
||||
print(`✓ find_dirs: Found ${found_dirs.len()} directories`);
|
||||
|
||||
// Test chdir function
|
||||
// Save current directory path before changing
|
||||
let chdir_result = chdir(test_dir);
|
||||
print(`✓ chdir: ${chdir_result}`);
|
||||
|
||||
// Change back to parent directory
|
||||
chdir("..");
|
||||
|
||||
// Test rsync function (if available)
|
||||
let rsync_dir = test_dir + "/rsync_dest";
|
||||
mkdir(rsync_dir);
|
||||
let rsync_result = rsync(test_dir, rsync_dir);
|
||||
print(`✓ rsync: ${rsync_result}`);
|
||||
|
||||
// Test delete function
|
||||
let delete_file_result = delete(test_file);
|
||||
assert_true(!exist(test_file), "File deletion failed");
|
||||
print(`✓ delete (file): ${delete_file_result}`);
|
||||
|
||||
// Clean up
|
||||
delete(moved_file);
|
||||
delete(sub_dir);
|
||||
delete(rsync_dir);
|
||||
delete(test_dir);
|
||||
assert_true(!exist(test_dir), "Directory deletion failed");
|
||||
print(`✓ delete (directory): Directory cleaned up`);
|
||||
|
||||
print("All file system tests completed successfully!");
|
@@ -1,53 +0,0 @@
|
||||
// 02_download_operations.rhai
|
||||
// Tests for download operations in the OS module
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a test directory
|
||||
let test_dir = "rhai_test_download";
|
||||
mkdir(test_dir);
|
||||
print(`Created test directory: ${test_dir}`);
|
||||
|
||||
// Test which function to ensure curl is available
|
||||
let curl_path = which("curl");
|
||||
if curl_path == "" {
|
||||
print("Warning: curl not found, download tests may fail");
|
||||
} else {
|
||||
print(`✓ which: curl found at ${curl_path}`);
|
||||
}
|
||||
|
||||
// Test cmd_ensure_exists function
|
||||
let ensure_result = cmd_ensure_exists("curl");
|
||||
print(`✓ cmd_ensure_exists: ${ensure_result}`);
|
||||
|
||||
// Test download function with a small file
|
||||
let download_url = "https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT";
|
||||
let download_dest = test_dir + "/license.txt";
|
||||
let min_size_kb = 1; // Minimum size in KB
|
||||
|
||||
print(`Downloading ${download_url}...`);
|
||||
let download_result = download_file(download_url, download_dest, min_size_kb);
|
||||
assert_true(exist(download_dest), "Download failed");
|
||||
print(`✓ download_file: ${download_result}`);
|
||||
|
||||
// Verify the downloaded file
|
||||
let file_content = file_read(download_dest);
|
||||
assert_true(file_content.contains("Permission is hereby granted"), "Downloaded file content is incorrect");
|
||||
print("✓ Downloaded file content verified");
|
||||
|
||||
// Test chmod_exec function
|
||||
let chmod_result = chmod_exec(download_dest);
|
||||
print(`✓ chmod_exec: ${chmod_result}`);
|
||||
|
||||
// Clean up
|
||||
delete(test_dir);
|
||||
assert_true(!exist(test_dir), "Directory deletion failed");
|
||||
print(`✓ Cleanup: Directory ${test_dir} removed`);
|
||||
|
||||
print("All download tests completed successfully!");
|
@@ -1,56 +0,0 @@
|
||||
// 03_package_operations.rhai
|
||||
// Tests for package management operations in the OS module
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Test package_platform function
|
||||
let platform = package_platform();
|
||||
print(`Current platform: ${platform}`);
|
||||
|
||||
// Test package_set_debug function
|
||||
let debug_enabled = package_set_debug(true);
|
||||
assert_true(debug_enabled, "Debug mode should be enabled");
|
||||
print("✓ package_set_debug: Debug mode enabled");
|
||||
|
||||
// Disable debug mode for remaining tests
|
||||
package_set_debug(false);
|
||||
|
||||
// Test package_is_installed function with a package that should exist on most systems
|
||||
let common_packages = ["bash", "curl", "grep"];
|
||||
let found_package = false;
|
||||
|
||||
for pkg in common_packages {
|
||||
let is_installed = package_is_installed(pkg);
|
||||
if is_installed {
|
||||
print(`✓ package_is_installed: ${pkg} is installed`);
|
||||
found_package = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !found_package {
|
||||
print("Warning: None of the common packages were found installed");
|
||||
}
|
||||
|
||||
// Test package_search function with a common term
|
||||
// Note: This might be slow and produce a lot of output
|
||||
print("Testing package_search (this might take a moment)...");
|
||||
let search_results = package_search("lib");
|
||||
print(`✓ package_search: Found ${search_results.len()} packages containing 'lib'`);
|
||||
|
||||
// Test package_list function
|
||||
// Note: This might be slow and produce a lot of output
|
||||
print("Testing package_list (this might take a moment)...");
|
||||
let installed_packages = package_list();
|
||||
print(`✓ package_list: Found ${installed_packages.len()} installed packages`);
|
||||
|
||||
// Note: We're not testing package_install, package_remove, package_update, or package_upgrade
|
||||
// as they require root privileges and could modify the system state
|
||||
|
||||
print("All package management tests completed successfully!");
|
@@ -1,148 +0,0 @@
|
||||
// run_all_tests.rhai
|
||||
// Runs all OS module tests
|
||||
|
||||
print("=== Running OS Module Tests ===");
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Run each test directly
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
// Test 1: File Operations
|
||||
print("\n--- Running File Operations Tests ---");
|
||||
try {
|
||||
// Create a test directory structure
|
||||
let test_dir = "rhai_test_fs";
|
||||
let sub_dir = test_dir + "/subdir";
|
||||
|
||||
// Test mkdir function
|
||||
print("Testing mkdir...");
|
||||
let mkdir_result = mkdir(test_dir);
|
||||
assert_true(exist(test_dir), "Directory creation failed");
|
||||
print(`✓ mkdir: ${mkdir_result}`);
|
||||
|
||||
// Test nested directory creation
|
||||
let nested_result = mkdir(sub_dir);
|
||||
assert_true(exist(sub_dir), "Nested directory creation failed");
|
||||
print(`✓ mkdir (nested): ${nested_result}`);
|
||||
|
||||
// Test file_write function
|
||||
let test_file = test_dir + "/test.txt";
|
||||
let file_content = "This is a test file created by Rhai test script.";
|
||||
let write_result = file_write(test_file, file_content);
|
||||
assert_true(exist(test_file), "File creation failed");
|
||||
print(`✓ file_write: ${write_result}`);
|
||||
|
||||
// Test file_read function
|
||||
let read_content = file_read(test_file);
|
||||
assert_true(read_content == file_content, "File content doesn't match");
|
||||
print(`✓ file_read: Content matches`);
|
||||
|
||||
// Test file_size function
|
||||
let size = file_size(test_file);
|
||||
assert_true(size > 0, "File size should be greater than 0");
|
||||
print(`✓ file_size: ${size} bytes`);
|
||||
|
||||
// Clean up
|
||||
delete(test_file);
|
||||
delete(sub_dir);
|
||||
delete(test_dir);
|
||||
assert_true(!exist(test_dir), "Directory deletion failed");
|
||||
print(`✓ delete: Directory cleaned up`);
|
||||
|
||||
print("--- File Operations Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in File Operations Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
|
||||
// Test 2: Download Operations
|
||||
print("\n--- Running Download Operations Tests ---");
|
||||
try {
|
||||
// Create a test directory
|
||||
let test_dir = "rhai_test_download";
|
||||
mkdir(test_dir);
|
||||
print(`Created test directory: ${test_dir}`);
|
||||
|
||||
// Test which function to ensure curl is available
|
||||
let curl_path = which("curl");
|
||||
if curl_path == "" {
|
||||
print("Warning: curl not found, download tests may fail");
|
||||
} else {
|
||||
print(`✓ which: curl found at ${curl_path}`);
|
||||
}
|
||||
|
||||
// Test cmd_ensure_exists function
|
||||
let ensure_result = cmd_ensure_exists("curl");
|
||||
print(`✓ cmd_ensure_exists: ${ensure_result}`);
|
||||
|
||||
// Test download function with a small file
|
||||
let download_url = "https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT";
|
||||
let download_dest = test_dir + "/license.txt";
|
||||
let min_size_kb = 1; // Minimum size in KB
|
||||
|
||||
print(`Downloading ${download_url}...`);
|
||||
let download_result = download_file(download_url, download_dest, min_size_kb);
|
||||
assert_true(exist(download_dest), "Download failed");
|
||||
print(`✓ download_file: ${download_result}`);
|
||||
|
||||
// Verify the downloaded file
|
||||
let file_content = file_read(download_dest);
|
||||
assert_true(file_content.contains("Permission is hereby granted"), "Downloaded file content is incorrect");
|
||||
print("✓ Downloaded file content verified");
|
||||
|
||||
// Clean up
|
||||
delete(test_dir);
|
||||
assert_true(!exist(test_dir), "Directory deletion failed");
|
||||
print(`✓ Cleanup: Directory ${test_dir} removed`);
|
||||
|
||||
print("--- Download Operations Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Download Operations Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
|
||||
// Test 3: Package Operations
|
||||
print("\n--- Running Package Operations Tests ---");
|
||||
try {
|
||||
// Test package_platform function
|
||||
let platform = package_platform();
|
||||
print(`Current platform: ${platform}`);
|
||||
|
||||
// Test package_set_debug function
|
||||
let debug_enabled = package_set_debug(true);
|
||||
assert_true(debug_enabled, "Debug mode should be enabled");
|
||||
print("✓ package_set_debug: Debug mode enabled");
|
||||
|
||||
// Disable debug mode for remaining tests
|
||||
package_set_debug(false);
|
||||
|
||||
print("--- Package Operations Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Package Operations Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
|
||||
print("\n=== Test Summary ===");
|
||||
print(`Passed: ${passed}`);
|
||||
print(`Failed: ${failed}`);
|
||||
print(`Total: ${passed + failed}`);
|
||||
|
||||
if failed == 0 {
|
||||
print("\n✅ All tests passed!");
|
||||
} else {
|
||||
print("\n❌ Some tests failed!");
|
||||
}
|
||||
|
||||
// Return the number of failed tests (0 means success)
|
||||
failed;
|
@@ -1,106 +0,0 @@
|
||||
// 01_postgres_connection.rhai
|
||||
// Tests for PostgreSQL client connection and basic operations
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if PostgreSQL is available
|
||||
fn is_postgres_available() {
|
||||
try {
|
||||
// Try to execute a simple connection
|
||||
let connect_result = pg_connect();
|
||||
return connect_result;
|
||||
} catch(err) {
|
||||
print(`PostgreSQL connection error: ${err}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing PostgreSQL Client Connection ===");
|
||||
|
||||
// Check if PostgreSQL is available
|
||||
let postgres_available = is_postgres_available();
|
||||
if !postgres_available {
|
||||
print("PostgreSQL server is not available. Skipping PostgreSQL tests.");
|
||||
// Exit gracefully without error
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ PostgreSQL server is available");
|
||||
|
||||
// Test pg_ping function
|
||||
print("Testing pg_ping()...");
|
||||
let ping_result = pg_ping();
|
||||
assert_true(ping_result, "PING should return true");
|
||||
print(`✓ pg_ping(): Returned ${ping_result}`);
|
||||
|
||||
// Test pg_execute function
|
||||
print("Testing pg_execute()...");
|
||||
let test_table = "rhai_test_table";
|
||||
|
||||
// Create a test table
|
||||
let create_table_query = `
|
||||
CREATE TABLE IF NOT EXISTS ${test_table} (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
value INTEGER
|
||||
)
|
||||
`;
|
||||
|
||||
let create_result = pg_execute(create_table_query);
|
||||
assert_true(create_result >= 0, "CREATE TABLE operation should succeed");
|
||||
print(`✓ pg_execute(): Successfully created table ${test_table}`);
|
||||
|
||||
// Insert a test row
|
||||
let insert_query = `
|
||||
INSERT INTO ${test_table} (name, value)
|
||||
VALUES ('test_name', 42)
|
||||
`;
|
||||
|
||||
let insert_result = pg_execute(insert_query);
|
||||
assert_true(insert_result > 0, "INSERT operation should succeed");
|
||||
print(`✓ pg_execute(): Successfully inserted row into ${test_table}`);
|
||||
|
||||
// Test pg_query function
|
||||
print("Testing pg_query()...");
|
||||
let select_query = `
|
||||
SELECT * FROM ${test_table}
|
||||
`;
|
||||
|
||||
let select_result = pg_query(select_query);
|
||||
assert_true(select_result.len() > 0, "SELECT should return at least one row");
|
||||
print(`✓ pg_query(): Successfully retrieved ${select_result.len()} rows from ${test_table}`);
|
||||
|
||||
// Test pg_query_one function
|
||||
print("Testing pg_query_one()...");
|
||||
let select_one_query = `
|
||||
SELECT * FROM ${test_table} LIMIT 1
|
||||
`;
|
||||
|
||||
let select_one_result = pg_query_one(select_one_query);
|
||||
assert_true(select_one_result["name"] == "test_name", "SELECT ONE should return the correct name");
|
||||
assert_true(select_one_result["value"] == "42", "SELECT ONE should return the correct value");
|
||||
print(`✓ pg_query_one(): Successfully retrieved row with name=${select_one_result["name"]} and value=${select_one_result["value"]}`);
|
||||
|
||||
// Clean up
|
||||
print("Cleaning up...");
|
||||
let drop_table_query = `
|
||||
DROP TABLE IF EXISTS ${test_table}
|
||||
`;
|
||||
|
||||
let drop_result = pg_execute(drop_table_query);
|
||||
assert_true(drop_result >= 0, "DROP TABLE operation should succeed");
|
||||
print(`✓ pg_execute(): Successfully dropped table ${test_table}`);
|
||||
|
||||
// Test pg_reset function
|
||||
print("Testing pg_reset()...");
|
||||
let reset_result = pg_reset();
|
||||
assert_true(reset_result, "RESET should return true");
|
||||
print(`✓ pg_reset(): Successfully reset PostgreSQL client`);
|
||||
|
||||
print("All PostgreSQL connection tests completed successfully!");
|
@@ -1,164 +0,0 @@
|
||||
// PostgreSQL Installer Test
|
||||
//
|
||||
// This test script demonstrates how to use the PostgreSQL installer module to:
|
||||
// - Install PostgreSQL using nerdctl
|
||||
// - Create a database
|
||||
// - Execute SQL scripts
|
||||
// - Check if PostgreSQL is running
|
||||
//
|
||||
// Prerequisites:
|
||||
// - nerdctl must be installed and working
|
||||
// - Docker images must be accessible
|
||||
|
||||
// Define utility functions
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Define test variables (will be used inside the test function)
|
||||
|
||||
// Function to check if nerdctl is available
|
||||
fn is_nerdctl_available() {
|
||||
try {
|
||||
// For testing purposes, we'll assume nerdctl is not available
|
||||
// In a real-world scenario, you would check if nerdctl is installed
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to clean up any existing PostgreSQL container
|
||||
fn cleanup_postgres() {
|
||||
try {
|
||||
// In a real-world scenario, you would use nerdctl to stop and remove the container
|
||||
// For this test, we'll just print a message
|
||||
print("Cleaned up existing PostgreSQL container (simulated)");
|
||||
} catch {
|
||||
// Ignore errors if container doesn't exist
|
||||
}
|
||||
}
|
||||
|
||||
// Main test function
|
||||
fn run_postgres_installer_test() {
|
||||
print("\n=== PostgreSQL Installer Test ===");
|
||||
|
||||
// Define test variables
|
||||
let container_name = "postgres-test";
|
||||
let postgres_version = "15";
|
||||
let postgres_port = 5433; // Use a non-default port to avoid conflicts
|
||||
let postgres_user = "testuser";
|
||||
let postgres_password = "testpassword";
|
||||
let test_db_name = "testdb";
|
||||
|
||||
// // Check if nerdctl is available
|
||||
// if !is_nerdctl_available() {
|
||||
// print("nerdctl is not available. Skipping PostgreSQL installer test.");
|
||||
// return 1; // Skip the test
|
||||
// }
|
||||
|
||||
// Clean up any existing PostgreSQL container
|
||||
cleanup_postgres();
|
||||
|
||||
// Test 1: Install PostgreSQL
|
||||
print("\n1. Installing PostgreSQL...");
|
||||
try {
|
||||
let install_result = pg_install(
|
||||
container_name,
|
||||
postgres_version,
|
||||
postgres_port,
|
||||
postgres_user,
|
||||
postgres_password
|
||||
);
|
||||
|
||||
assert_true(install_result, "PostgreSQL installation should succeed");
|
||||
print("✓ PostgreSQL installed successfully");
|
||||
|
||||
// Wait a bit for PostgreSQL to fully initialize
|
||||
print("Waiting for PostgreSQL to initialize...");
|
||||
// In a real-world scenario, you would wait for PostgreSQL to initialize
|
||||
// For this test, we'll just print a message
|
||||
print("Waited for PostgreSQL to initialize (simulated)")
|
||||
} catch(e) {
|
||||
print(`✗ Failed to install PostgreSQL: ${e}`);
|
||||
cleanup_postgres();
|
||||
return 1; // Test failed
|
||||
}
|
||||
|
||||
// Test 2: Check if PostgreSQL is running
|
||||
print("\n2. Checking if PostgreSQL is running...");
|
||||
try {
|
||||
let running = pg_is_running(container_name);
|
||||
assert_true(running, "PostgreSQL should be running");
|
||||
print("✓ PostgreSQL is running");
|
||||
} catch(e) {
|
||||
print(`✗ Failed to check if PostgreSQL is running: ${e}`);
|
||||
cleanup_postgres();
|
||||
return 1; // Test failed
|
||||
}
|
||||
|
||||
// Test 3: Create a database
|
||||
print("\n3. Creating a database...");
|
||||
try {
|
||||
let create_result = pg_create_database(container_name, test_db_name);
|
||||
assert_true(create_result, "Database creation should succeed");
|
||||
print(`✓ Database '${test_db_name}' created successfully`);
|
||||
} catch(e) {
|
||||
print(`✗ Failed to create database: ${e}`);
|
||||
cleanup_postgres();
|
||||
return 1; // Test failed
|
||||
}
|
||||
|
||||
// Test 4: Execute SQL script
|
||||
print("\n4. Executing SQL script...");
|
||||
try {
|
||||
// Create a table
|
||||
let create_table_sql = `
|
||||
CREATE TABLE test_table (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
value INTEGER
|
||||
);
|
||||
`;
|
||||
|
||||
let result = pg_execute_sql(container_name, test_db_name, create_table_sql);
|
||||
print("✓ Created table successfully");
|
||||
|
||||
// Insert data
|
||||
let insert_sql = `
|
||||
INSERT INTO test_table (name, value) VALUES
|
||||
('test1', 100),
|
||||
('test2', 200),
|
||||
('test3', 300);
|
||||
`;
|
||||
|
||||
result = pg_execute_sql(container_name, test_db_name, insert_sql);
|
||||
print("✓ Inserted data successfully");
|
||||
|
||||
// Query data
|
||||
let query_sql = "SELECT * FROM test_table ORDER BY id;";
|
||||
result = pg_execute_sql(container_name, test_db_name, query_sql);
|
||||
print("✓ Queried data successfully");
|
||||
print(`Query result: ${result}`);
|
||||
} catch(e) {
|
||||
print(`✗ Failed to execute SQL script: ${e}`);
|
||||
cleanup_postgres();
|
||||
return 1; // Test failed
|
||||
}
|
||||
|
||||
// Clean up
|
||||
print("\nCleaning up...");
|
||||
cleanup_postgres();
|
||||
|
||||
print("\n=== PostgreSQL Installer Test Completed Successfully ===");
|
||||
return 0; // Test passed
|
||||
}
|
||||
|
||||
// Run the test
|
||||
let result = run_postgres_installer_test();
|
||||
|
||||
// Return the result
|
||||
result
|
@@ -1,61 +0,0 @@
|
||||
// PostgreSQL Installer Test (Mock)
|
||||
//
|
||||
// This test script simulates the PostgreSQL installer module tests
|
||||
// without actually calling the PostgreSQL functions.
|
||||
|
||||
// Define utility functions
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Main test function
|
||||
fn run_postgres_installer_test() {
|
||||
print("\n=== PostgreSQL Installer Test (Mock) ===");
|
||||
|
||||
// Define test variables
|
||||
let container_name = "postgres-test";
|
||||
let postgres_version = "15";
|
||||
let postgres_port = 5433; // Use a non-default port to avoid conflicts
|
||||
let postgres_user = "testuser";
|
||||
let postgres_password = "testpassword";
|
||||
let test_db_name = "testdb";
|
||||
|
||||
// Clean up any existing PostgreSQL container
|
||||
print("Cleaned up existing PostgreSQL container (simulated)");
|
||||
|
||||
// Test 1: Install PostgreSQL
|
||||
print("\n1. Installing PostgreSQL...");
|
||||
print("✓ PostgreSQL installed successfully (simulated)");
|
||||
print("Waited for PostgreSQL to initialize (simulated)");
|
||||
|
||||
// Test 2: Check if PostgreSQL is running
|
||||
print("\n2. Checking if PostgreSQL is running...");
|
||||
print("✓ PostgreSQL is running (simulated)");
|
||||
|
||||
// Test 3: Create a database
|
||||
print("\n3. Creating a database...");
|
||||
print(`✓ Database '${test_db_name}' created successfully (simulated)`);
|
||||
|
||||
// Test 4: Execute SQL script
|
||||
print("\n4. Executing SQL script...");
|
||||
print("✓ Created table successfully (simulated)");
|
||||
print("✓ Inserted data successfully (simulated)");
|
||||
print("✓ Queried data successfully (simulated)");
|
||||
print("Query result: (simulated results)");
|
||||
|
||||
// Clean up
|
||||
print("\nCleaning up...");
|
||||
print("Cleaned up existing PostgreSQL container (simulated)");
|
||||
|
||||
print("\n=== PostgreSQL Installer Test Completed Successfully ===");
|
||||
return 0; // Test passed
|
||||
}
|
||||
|
||||
// Run the test
|
||||
let result = run_postgres_installer_test();
|
||||
|
||||
// Return the result
|
||||
result
|
@@ -1,101 +0,0 @@
|
||||
// PostgreSQL Installer Test (Simplified)
|
||||
//
|
||||
// This test script demonstrates how to use the PostgreSQL installer module to:
|
||||
// - Install PostgreSQL using nerdctl
|
||||
// - Create a database
|
||||
// - Execute SQL scripts
|
||||
// - Check if PostgreSQL is running
|
||||
|
||||
// Define test variables
|
||||
let container_name = "postgres-test";
|
||||
let postgres_version = "15";
|
||||
let postgres_port = 5433; // Use a non-default port to avoid conflicts
|
||||
let postgres_user = "testuser";
|
||||
let postgres_password = "testpassword";
|
||||
let test_db_name = "testdb";
|
||||
|
||||
// Main test function
|
||||
fn test_postgres_installer() {
|
||||
print("\n=== PostgreSQL Installer Test ===");
|
||||
|
||||
// Test 1: Install PostgreSQL
|
||||
print("\n1. Installing PostgreSQL...");
|
||||
try {
|
||||
let install_result = pg_install(
|
||||
container_name,
|
||||
postgres_version,
|
||||
postgres_port,
|
||||
postgres_user,
|
||||
postgres_password
|
||||
);
|
||||
|
||||
print(`PostgreSQL installation result: ${install_result}`);
|
||||
print("✓ PostgreSQL installed successfully");
|
||||
} catch(e) {
|
||||
print(`✗ Failed to install PostgreSQL: ${e}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Test 2: Check if PostgreSQL is running
|
||||
print("\n2. Checking if PostgreSQL is running...");
|
||||
try {
|
||||
let running = pg_is_running(container_name);
|
||||
print(`PostgreSQL running status: ${running}`);
|
||||
print("✓ PostgreSQL is running");
|
||||
} catch(e) {
|
||||
print(`✗ Failed to check if PostgreSQL is running: ${e}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Test 3: Create a database
|
||||
print("\n3. Creating a database...");
|
||||
try {
|
||||
let create_result = pg_create_database(container_name, test_db_name);
|
||||
print(`Database creation result: ${create_result}`);
|
||||
print(`✓ Database '${test_db_name}' created successfully`);
|
||||
} catch(e) {
|
||||
print(`✗ Failed to create database: ${e}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Test 4: Execute SQL script
|
||||
print("\n4. Executing SQL script...");
|
||||
try {
|
||||
// Create a table
|
||||
let create_table_sql = `
|
||||
CREATE TABLE test_table (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
value INTEGER
|
||||
);
|
||||
`;
|
||||
|
||||
let result = pg_execute_sql(container_name, test_db_name, create_table_sql);
|
||||
print("✓ Created table successfully");
|
||||
|
||||
// Insert data
|
||||
let insert_sql = `
|
||||
INSERT INTO test_table (name, value) VALUES
|
||||
('test1', 100),
|
||||
('test2', 200),
|
||||
('test3', 300);
|
||||
`;
|
||||
|
||||
result = pg_execute_sql(container_name, test_db_name, insert_sql);
|
||||
print("✓ Inserted data successfully");
|
||||
|
||||
// Query data
|
||||
let query_sql = "SELECT * FROM test_table ORDER BY id;";
|
||||
result = pg_execute_sql(container_name, test_db_name, query_sql);
|
||||
print("✓ Queried data successfully");
|
||||
print(`Query result: ${result}`);
|
||||
} catch(e) {
|
||||
print(`✗ Failed to execute SQL script: ${e}`);
|
||||
return;
|
||||
}
|
||||
|
||||
print("\n=== PostgreSQL Installer Test Completed Successfully ===");
|
||||
}
|
||||
|
||||
// Run the test
|
||||
test_postgres_installer();
|
@@ -1,82 +0,0 @@
|
||||
// PostgreSQL Installer Example
|
||||
//
|
||||
// This example demonstrates how to use the PostgreSQL installer module to:
|
||||
// - Install PostgreSQL using nerdctl
|
||||
// - Create a database
|
||||
// - Execute SQL scripts
|
||||
// - Check if PostgreSQL is running
|
||||
//
|
||||
// Prerequisites:
|
||||
// - nerdctl must be installed and working
|
||||
// - Docker images must be accessible
|
||||
|
||||
// Define variables
|
||||
let container_name = "postgres-example";
|
||||
let postgres_version = "15";
|
||||
let postgres_port = 5432;
|
||||
let postgres_user = "exampleuser";
|
||||
let postgres_password = "examplepassword";
|
||||
let db_name = "exampledb";
|
||||
|
||||
// Install PostgreSQL
|
||||
print("Installing PostgreSQL...");
|
||||
try {
|
||||
let install_result = pg_install(
|
||||
container_name,
|
||||
postgres_version,
|
||||
postgres_port,
|
||||
postgres_user,
|
||||
postgres_password
|
||||
);
|
||||
|
||||
print("PostgreSQL installed successfully!");
|
||||
|
||||
// Check if PostgreSQL is running
|
||||
print("\nChecking if PostgreSQL is running...");
|
||||
let running = pg_is_running(container_name);
|
||||
|
||||
if (running) {
|
||||
print("PostgreSQL is running!");
|
||||
|
||||
// Create a database
|
||||
print("\nCreating a database...");
|
||||
let create_result = pg_create_database(container_name, db_name);
|
||||
print(`Database '${db_name}' created successfully!`);
|
||||
|
||||
// Create a table
|
||||
print("\nCreating a table...");
|
||||
let create_table_sql = `
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL
|
||||
);
|
||||
`;
|
||||
|
||||
let result = pg_execute_sql(container_name, db_name, create_table_sql);
|
||||
print("Table created successfully!");
|
||||
|
||||
// Insert data
|
||||
print("\nInserting data...");
|
||||
let insert_sql = `
|
||||
INSERT INTO users (name, email) VALUES
|
||||
('John Doe', 'john@example.com'),
|
||||
('Jane Smith', 'jane@example.com');
|
||||
`;
|
||||
|
||||
result = pg_execute_sql(container_name, db_name, insert_sql);
|
||||
print("Data inserted successfully!");
|
||||
|
||||
// Query data
|
||||
print("\nQuerying data...");
|
||||
let query_sql = "SELECT * FROM users;";
|
||||
result = pg_execute_sql(container_name, db_name, query_sql);
|
||||
print(`Query result: ${result}`);
|
||||
} else {
|
||||
print("PostgreSQL is not running!");
|
||||
}
|
||||
} catch(e) {
|
||||
print(`Error: ${e}`);
|
||||
}
|
||||
|
||||
print("\nExample completed!");
|
@@ -1,159 +0,0 @@
|
||||
// run_all_tests.rhai
|
||||
// Runs all PostgreSQL client module tests
|
||||
|
||||
print("=== Running PostgreSQL Client Module Tests ===");
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if PostgreSQL is available
|
||||
fn is_postgres_available() {
|
||||
try {
|
||||
// Try to execute a simple connection
|
||||
let connect_result = pg_connect();
|
||||
return connect_result;
|
||||
} catch(err) {
|
||||
print(`PostgreSQL connection error: ${err}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if nerdctl is available
|
||||
fn is_nerdctl_available() {
|
||||
try {
|
||||
// For testing purposes, we'll assume nerdctl is not available
|
||||
// In a real-world scenario, you would check if nerdctl is installed
|
||||
return false;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Run each test directly
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
let skipped = 0;
|
||||
|
||||
// Check if PostgreSQL is available
|
||||
let postgres_available = is_postgres_available();
|
||||
if !postgres_available {
|
||||
print("PostgreSQL server is not available. Skipping basic PostgreSQL tests.");
|
||||
skipped += 1; // Skip the test
|
||||
} else {
|
||||
// Test 1: PostgreSQL Connection
|
||||
print("\n--- Running PostgreSQL Connection Tests ---");
|
||||
try {
|
||||
// Test pg_ping function
|
||||
print("Testing pg_ping()...");
|
||||
let ping_result = pg_ping();
|
||||
assert_true(ping_result, "PING should return true");
|
||||
print(`✓ pg_ping(): Returned ${ping_result}`);
|
||||
|
||||
// Test pg_execute function
|
||||
print("Testing pg_execute()...");
|
||||
let test_table = "rhai_test_table";
|
||||
|
||||
// Create a test table
|
||||
let create_table_query = `
|
||||
CREATE TABLE IF NOT EXISTS ${test_table} (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
value INTEGER
|
||||
)
|
||||
`;
|
||||
|
||||
let create_result = pg_execute(create_table_query);
|
||||
assert_true(create_result >= 0, "CREATE TABLE operation should succeed");
|
||||
print(`✓ pg_execute(): Successfully created table ${test_table}`);
|
||||
|
||||
// Insert a test row
|
||||
let insert_query = `
|
||||
INSERT INTO ${test_table} (name, value)
|
||||
VALUES ('test_name', 42)
|
||||
`;
|
||||
|
||||
let insert_result = pg_execute(insert_query);
|
||||
assert_true(insert_result > 0, "INSERT operation should succeed");
|
||||
print(`✓ pg_execute(): Successfully inserted row into ${test_table}`);
|
||||
|
||||
// Test pg_query function
|
||||
print("Testing pg_query()...");
|
||||
let select_query = `
|
||||
SELECT * FROM ${test_table}
|
||||
`;
|
||||
|
||||
let select_result = pg_query(select_query);
|
||||
assert_true(select_result.len() > 0, "SELECT should return at least one row");
|
||||
print(`✓ pg_query(): Successfully retrieved ${select_result.len()} rows from ${test_table}`);
|
||||
|
||||
// Clean up
|
||||
print("Cleaning up...");
|
||||
let drop_table_query = `
|
||||
DROP TABLE IF EXISTS ${test_table}
|
||||
`;
|
||||
|
||||
let drop_result = pg_execute(drop_table_query);
|
||||
assert_true(drop_result >= 0, "DROP TABLE operation should succeed");
|
||||
print(`✓ pg_execute(): Successfully dropped table ${test_table}`);
|
||||
|
||||
print("--- PostgreSQL Connection Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in PostgreSQL Connection Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Test 2: PostgreSQL Installer
|
||||
// Check if nerdctl is available
|
||||
let nerdctl_available = is_nerdctl_available();
|
||||
if !nerdctl_available {
|
||||
print("nerdctl is not available. Running mock PostgreSQL installer tests.");
|
||||
try {
|
||||
// Run the mock installer test
|
||||
let installer_test_result = 0; // Simulate success
|
||||
print("\n--- Running PostgreSQL Installer Tests (Mock) ---");
|
||||
print("✓ PostgreSQL installed successfully (simulated)");
|
||||
print("✓ Database created successfully (simulated)");
|
||||
print("✓ SQL executed successfully (simulated)");
|
||||
print("--- PostgreSQL Installer Tests completed successfully (simulated) ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in PostgreSQL Installer Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
} else {
|
||||
print("\n--- Running PostgreSQL Installer Tests ---");
|
||||
try {
|
||||
// For testing purposes, we'll assume the installer tests pass
|
||||
print("--- PostgreSQL Installer Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in PostgreSQL Installer Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
print("\n=== Test Summary ===");
|
||||
print(`Passed: ${passed}`);
|
||||
print(`Failed: ${failed}`);
|
||||
print(`Skipped: ${skipped}`);
|
||||
print(`Total: ${passed + failed + skipped}`);
|
||||
|
||||
if failed == 0 {
|
||||
if skipped > 0 {
|
||||
print("\n⚠️ All tests skipped or passed!");
|
||||
} else {
|
||||
print("\n✅ All tests passed!");
|
||||
}
|
||||
} else {
|
||||
print("\n❌ Some tests failed!");
|
||||
}
|
||||
|
||||
// Return the number of failed tests (0 means success)
|
||||
failed;
|
@@ -1,93 +0,0 @@
|
||||
// Test script to check if the PostgreSQL functions are registered
|
||||
|
||||
// Try to call the basic PostgreSQL functions
|
||||
try {
|
||||
print("Trying to call pg_connect()...");
|
||||
let result = pg_connect();
|
||||
print("pg_connect result: " + result);
|
||||
} catch(e) {
|
||||
print("Error calling pg_connect: " + e);
|
||||
}
|
||||
|
||||
// Try to call the pg_ping function
|
||||
try {
|
||||
print("\nTrying to call pg_ping()...");
|
||||
let result = pg_ping();
|
||||
print("pg_ping result: " + result);
|
||||
} catch(e) {
|
||||
print("Error calling pg_ping: " + e);
|
||||
}
|
||||
|
||||
// Try to call the pg_reset function
|
||||
try {
|
||||
print("\nTrying to call pg_reset()...");
|
||||
let result = pg_reset();
|
||||
print("pg_reset result: " + result);
|
||||
} catch(e) {
|
||||
print("Error calling pg_reset: " + e);
|
||||
}
|
||||
|
||||
// Try to call the pg_execute function
|
||||
try {
|
||||
print("\nTrying to call pg_execute()...");
|
||||
let result = pg_execute("SELECT 1");
|
||||
print("pg_execute result: " + result);
|
||||
} catch(e) {
|
||||
print("Error calling pg_execute: " + e);
|
||||
}
|
||||
|
||||
// Try to call the pg_query function
|
||||
try {
|
||||
print("\nTrying to call pg_query()...");
|
||||
let result = pg_query("SELECT 1");
|
||||
print("pg_query result: " + result);
|
||||
} catch(e) {
|
||||
print("Error calling pg_query: " + e);
|
||||
}
|
||||
|
||||
// Try to call the pg_query_one function
|
||||
try {
|
||||
print("\nTrying to call pg_query_one()...");
|
||||
let result = pg_query_one("SELECT 1");
|
||||
print("pg_query_one result: " + result);
|
||||
} catch(e) {
|
||||
print("Error calling pg_query_one: " + e);
|
||||
}
|
||||
|
||||
// Try to call the pg_install function
|
||||
try {
|
||||
print("\nTrying to call pg_install()...");
|
||||
let result = pg_install("postgres-test", "15", 5433, "testuser", "testpassword");
|
||||
print("pg_install result: " + result);
|
||||
} catch(e) {
|
||||
print("Error calling pg_install: " + e);
|
||||
}
|
||||
|
||||
// Try to call the pg_create_database function
|
||||
try {
|
||||
print("\nTrying to call pg_create_database()...");
|
||||
let result = pg_create_database("postgres-test", "testdb");
|
||||
print("pg_create_database result: " + result);
|
||||
} catch(e) {
|
||||
print("Error calling pg_create_database: " + e);
|
||||
}
|
||||
|
||||
// Try to call the pg_execute_sql function
|
||||
try {
|
||||
print("\nTrying to call pg_execute_sql()...");
|
||||
let result = pg_execute_sql("postgres-test", "testdb", "SELECT 1");
|
||||
print("pg_execute_sql result: " + result);
|
||||
} catch(e) {
|
||||
print("Error calling pg_execute_sql: " + e);
|
||||
}
|
||||
|
||||
// Try to call the pg_is_running function
|
||||
try {
|
||||
print("\nTrying to call pg_is_running()...");
|
||||
let result = pg_is_running("postgres-test");
|
||||
print("pg_is_running result: " + result);
|
||||
} catch(e) {
|
||||
print("Error calling pg_is_running: " + e);
|
||||
}
|
||||
|
||||
print("\nTest completed!");
|
@@ -1,24 +0,0 @@
|
||||
// Simple test script to verify that the Rhai engine is working
|
||||
|
||||
print("Hello, world!");
|
||||
|
||||
// Try to access the PostgreSQL installer functions
|
||||
print("\nTrying to access PostgreSQL installer functions...");
|
||||
|
||||
// Check if the pg_install function is defined
|
||||
print("pg_install function is defined: " + is_def_fn("pg_install"));
|
||||
|
||||
// Print the available functions
|
||||
print("\nAvailable functions:");
|
||||
print("pg_connect: " + is_def_fn("pg_connect"));
|
||||
print("pg_ping: " + is_def_fn("pg_ping"));
|
||||
print("pg_reset: " + is_def_fn("pg_reset"));
|
||||
print("pg_execute: " + is_def_fn("pg_execute"));
|
||||
print("pg_query: " + is_def_fn("pg_query"));
|
||||
print("pg_query_one: " + is_def_fn("pg_query_one"));
|
||||
print("pg_install: " + is_def_fn("pg_install"));
|
||||
print("pg_create_database: " + is_def_fn("pg_create_database"));
|
||||
print("pg_execute_sql: " + is_def_fn("pg_execute_sql"));
|
||||
print("pg_is_running: " + is_def_fn("pg_is_running"));
|
||||
|
||||
print("\nTest completed successfully!");
|
@@ -1,22 +0,0 @@
|
||||
// Simple test script to verify that the Rhai engine is working
|
||||
|
||||
print("Hello, world!");
|
||||
|
||||
// Try to access the PostgreSQL installer functions
|
||||
print("\nTrying to access PostgreSQL installer functions...");
|
||||
|
||||
// Try to call the pg_install function
|
||||
try {
|
||||
let result = pg_install(
|
||||
"postgres-test",
|
||||
"15",
|
||||
5433,
|
||||
"testuser",
|
||||
"testpassword"
|
||||
);
|
||||
print("pg_install result: " + result);
|
||||
} catch(e) {
|
||||
print("Error calling pg_install: " + e);
|
||||
}
|
||||
|
||||
print("\nTest completed!");
|
@@ -1,61 +0,0 @@
|
||||
// 01_command_execution.rhai
|
||||
// Tests for command execution in the Process module
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Basic Command Execution ===");
|
||||
|
||||
// Test running a simple command
|
||||
print("Testing run() with a simple command...");
|
||||
let result = run("echo Hello, World!").execute();
|
||||
assert_true(result.success, "Command should succeed");
|
||||
assert_true(result.stdout.contains("Hello, World!"), "Command output should contain the expected text");
|
||||
print(`✓ run().execute(): Command executed successfully`);
|
||||
|
||||
// Test running a command with arguments
|
||||
print("Testing run() with command arguments...");
|
||||
let result_with_args = run("echo Hello from Rhai tests").execute();
|
||||
assert_true(result_with_args.success, "Command with arguments should succeed");
|
||||
assert_true(result_with_args.stdout.contains("Hello from Rhai tests"), "Command output should contain the expected text");
|
||||
print(`✓ run().execute(): Command with arguments executed successfully`);
|
||||
|
||||
// Test running a command with environment variables
|
||||
print("Testing run() with environment variables...");
|
||||
let env_result = run("echo $HOME").execute();
|
||||
assert_true(env_result.success, "Command with environment variables should succeed");
|
||||
assert_true(env_result.stdout.trim() != "", "Environment variable should be expanded");
|
||||
print(`✓ run().execute(): Command with environment variables executed successfully`);
|
||||
|
||||
// Test running a multiline script
|
||||
print("Testing run() with a multiline script...");
|
||||
let script_result = run(`
|
||||
echo "Line 1"
|
||||
echo "Line 2"
|
||||
echo "Line 3"
|
||||
`).execute();
|
||||
assert_true(script_result.success, "Multiline script should succeed");
|
||||
assert_true(script_result.stdout.contains("Line 1") && script_result.stdout.contains("Line 2") && script_result.stdout.contains("Line 3"),
|
||||
"Script output should contain all lines");
|
||||
print(`✓ run().execute(): Multiline script executed successfully`);
|
||||
|
||||
// Test which function
|
||||
print("Testing which() function...");
|
||||
let bash_path = which("bash");
|
||||
assert_true(bash_path != "", "bash should be found in PATH");
|
||||
print(`✓ which(): Found bash at ${bash_path}`);
|
||||
|
||||
// Test a command that doesn't exist
|
||||
let nonexistent_cmd = which("this_command_does_not_exist_12345");
|
||||
if nonexistent_cmd == "" {
|
||||
print(`✓ which(): Correctly reported that nonexistent command was not found`);
|
||||
} else {
|
||||
print(`Note: Unexpectedly found command at ${nonexistent_cmd}`);
|
||||
}
|
||||
|
||||
print("All command execution tests completed successfully!");
|
@@ -1,54 +0,0 @@
|
||||
// 02_process_management.rhai
|
||||
// Tests for process management functions in the Process module
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Process Management Functions ===");
|
||||
|
||||
// Test process_list function
|
||||
print("Testing process_list() function...");
|
||||
let all_processes = process_list("");
|
||||
assert_true(all_processes.len() > 0, "There should be at least one running process");
|
||||
print(`✓ process_list(): Found ${all_processes.len()} processes`);
|
||||
|
||||
// Test process properties
|
||||
print("Testing process properties...");
|
||||
let first_process = all_processes[0];
|
||||
assert_true(first_process.pid > 0, "Process PID should be a positive number");
|
||||
assert_true(first_process.name.len() > 0, "Process name should not be empty");
|
||||
print(`✓ Process properties: PID=${first_process.pid}, Name=${first_process.name}, CPU=${first_process.cpu}%, Memory=${first_process.memory}`);
|
||||
|
||||
// Test process_list with a pattern
|
||||
print("Testing process_list() with a pattern...");
|
||||
// Use a pattern that's likely to match at least one process on most systems
|
||||
let pattern = "sh";
|
||||
let matching_processes = process_list(pattern);
|
||||
print(`Found ${matching_processes.len()} processes matching '${pattern}'`);
|
||||
if (matching_processes.len() > 0) {
|
||||
let matched_process = matching_processes[0];
|
||||
print(`✓ process_list(pattern): Found process ${matched_process.name} with PID ${matched_process.pid}`);
|
||||
} else {
|
||||
print(`Note: No processes found matching '${pattern}'. This is not necessarily an error.`);
|
||||
}
|
||||
|
||||
// Test process_get function
|
||||
// Note: We'll only test this if we found matching processes above
|
||||
if (matching_processes.len() == 1) {
|
||||
print("Testing process_get() function...");
|
||||
let process = process_get(pattern);
|
||||
assert_true(process.pid > 0, "Process PID should be a positive number");
|
||||
assert_true(process.name.contains(pattern), "Process name should contain the pattern");
|
||||
print(`✓ process_get(): Found process ${process.name} with PID ${process.pid}`);
|
||||
} else {
|
||||
print("Skipping process_get() test as it requires exactly one matching process");
|
||||
}
|
||||
|
||||
// Note: We won't test the kill function as it could disrupt the system
|
||||
|
||||
print("All process management tests completed successfully!");
|
@@ -1,76 +0,0 @@
|
||||
// run_all_tests.rhai
|
||||
// Runs all Process module tests
|
||||
|
||||
print("=== Running Process Module Tests ===");
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Run each test directly
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
// Test 1: Command Execution
|
||||
print("\n--- Running Command Execution Tests ---");
|
||||
try {
|
||||
// Test running a simple command
|
||||
print("Testing run() with a simple command...");
|
||||
let result = run("echo Hello, World!").execute();
|
||||
assert_true(result.success, "Command should succeed");
|
||||
assert_true(result.stdout.contains("Hello, World!"), "Command output should contain the expected text");
|
||||
print(`✓ run().execute(): Command executed successfully`);
|
||||
|
||||
// Test which function
|
||||
print("Testing which() function...");
|
||||
let bash_path = which("bash");
|
||||
assert_true(bash_path != "", "bash should be found in PATH");
|
||||
print(`✓ which(): Found bash at ${bash_path}`);
|
||||
|
||||
print("--- Command Execution Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Command Execution Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
|
||||
// Test 2: Process Management
|
||||
print("\n--- Running Process Management Tests ---");
|
||||
try {
|
||||
// Test process_list function
|
||||
print("Testing process_list() function...");
|
||||
let all_processes = process_list("");
|
||||
assert_true(all_processes.len() > 0, "There should be at least one running process");
|
||||
print(`✓ process_list(): Found ${all_processes.len()} processes`);
|
||||
|
||||
// Test process properties
|
||||
print("Testing process properties...");
|
||||
let first_process = all_processes[0];
|
||||
assert_true(first_process.pid > 0, "Process PID should be a positive number");
|
||||
assert_true(first_process.name.len() > 0, "Process name should not be empty");
|
||||
print(`✓ Process properties: PID=${first_process.pid}, Name=${first_process.name}`);
|
||||
|
||||
print("--- Process Management Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Process Management Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
|
||||
print("\n=== Test Summary ===");
|
||||
print(`Passed: ${passed}`);
|
||||
print(`Failed: ${failed}`);
|
||||
print(`Total: ${passed + failed}`);
|
||||
|
||||
if failed == 0 {
|
||||
print("\n✅ All tests passed!");
|
||||
} else {
|
||||
print("\n❌ Some tests failed!");
|
||||
}
|
||||
|
||||
// Return the number of failed tests (0 means success)
|
||||
failed;
|
@@ -1,68 +0,0 @@
|
||||
// 01_redis_connection.rhai
|
||||
// Tests for Redis client connection and basic operations
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if Redis is available
|
||||
fn is_redis_available() {
|
||||
try {
|
||||
// Try to execute a simple PING command
|
||||
let ping_result = redis_ping();
|
||||
return ping_result == "PONG";
|
||||
} catch(err) {
|
||||
print(`Redis connection error: ${err}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Redis Client Connection ===");
|
||||
|
||||
// Check if Redis is available
|
||||
let redis_available = is_redis_available();
|
||||
if !redis_available {
|
||||
print("Redis server is not available. Skipping Redis tests.");
|
||||
// Exit gracefully without error
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ Redis server is available");
|
||||
|
||||
// Test redis_ping function
|
||||
print("Testing redis_ping()...");
|
||||
let ping_result = redis_ping();
|
||||
assert_true(ping_result == "PONG", "PING should return PONG");
|
||||
print(`✓ redis_ping(): Returned ${ping_result}`);
|
||||
|
||||
// Test redis_set and redis_get functions
|
||||
print("Testing redis_set() and redis_get()...");
|
||||
let test_key = "rhai_test_key";
|
||||
let test_value = "Hello from Rhai test";
|
||||
|
||||
// Set a value
|
||||
let set_result = redis_set(test_key, test_value);
|
||||
assert_true(set_result, "SET operation should succeed");
|
||||
print(`✓ redis_set(): Successfully set key ${test_key}`);
|
||||
|
||||
// Get the value back
|
||||
let get_result = redis_get(test_key);
|
||||
assert_true(get_result == test_value, "GET should return the value we set");
|
||||
print(`✓ redis_get(): Successfully retrieved value for key ${test_key}`);
|
||||
|
||||
// Test redis_del function
|
||||
print("Testing redis_del()...");
|
||||
let del_result = redis_del(test_key);
|
||||
assert_true(del_result, "DEL operation should succeed");
|
||||
print(`✓ redis_del(): Successfully deleted key ${test_key}`);
|
||||
|
||||
// Verify the key was deleted
|
||||
let get_after_del = redis_get(test_key);
|
||||
assert_true(get_after_del == "", "Key should not exist after deletion");
|
||||
print("✓ Key was successfully deleted");
|
||||
|
||||
print("All Redis connection tests completed successfully!");
|
@@ -1,109 +0,0 @@
|
||||
// 02_redis_operations.rhai
|
||||
// Tests for advanced Redis operations
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if Redis is available
|
||||
fn is_redis_available() {
|
||||
try {
|
||||
// Try to execute a simple PING command
|
||||
let ping_result = redis_ping();
|
||||
return ping_result == "PONG";
|
||||
} catch(err) {
|
||||
print(`Redis connection error: ${err}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Advanced Redis Operations ===");
|
||||
|
||||
// Check if Redis is available
|
||||
let redis_available = is_redis_available();
|
||||
if !redis_available {
|
||||
print("Redis server is not available. Skipping Redis tests.");
|
||||
// Exit gracefully without error
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ Redis server is available");
|
||||
|
||||
// Test prefix for all keys to avoid conflicts
|
||||
let prefix = "rhai_test_";
|
||||
|
||||
// Test redis_hset and redis_hget functions
|
||||
print("Testing redis_hset() and redis_hget()...");
|
||||
let hash_key = prefix + "hash";
|
||||
let field1 = "field1";
|
||||
let value1 = "value1";
|
||||
let field2 = "field2";
|
||||
let value2 = "value2";
|
||||
|
||||
// Set hash fields
|
||||
let hset_result1 = redis_hset(hash_key, field1, value1);
|
||||
assert_true(hset_result1, "HSET operation should succeed for field1");
|
||||
let hset_result2 = redis_hset(hash_key, field2, value2);
|
||||
assert_true(hset_result2, "HSET operation should succeed for field2");
|
||||
print(`✓ redis_hset(): Successfully set fields in hash ${hash_key}`);
|
||||
|
||||
// Get hash fields
|
||||
let hget_result1 = redis_hget(hash_key, field1);
|
||||
assert_true(hget_result1 == value1, "HGET should return the value we set for field1");
|
||||
let hget_result2 = redis_hget(hash_key, field2);
|
||||
assert_true(hget_result2 == value2, "HGET should return the value we set for field2");
|
||||
print(`✓ redis_hget(): Successfully retrieved values from hash ${hash_key}`);
|
||||
|
||||
// Test redis_hgetall function
|
||||
print("Testing redis_hgetall()...");
|
||||
let hgetall_result = redis_hgetall(hash_key);
|
||||
assert_true(hgetall_result.len() == 2, "HGETALL should return 2 fields");
|
||||
assert_true(hgetall_result[field1] == value1, "HGETALL should include field1 with correct value");
|
||||
assert_true(hgetall_result[field2] == value2, "HGETALL should include field2 with correct value");
|
||||
print(`✓ redis_hgetall(): Successfully retrieved all fields from hash ${hash_key}`);
|
||||
|
||||
// Test redis_hdel function
|
||||
print("Testing redis_hdel()...");
|
||||
let hdel_result = redis_hdel(hash_key, field1);
|
||||
assert_true(hdel_result, "HDEL operation should succeed");
|
||||
print(`✓ redis_hdel(): Successfully deleted field from hash ${hash_key}`);
|
||||
|
||||
// Verify the field was deleted
|
||||
let hget_after_del = redis_hget(hash_key, field1);
|
||||
assert_true(hget_after_del == "", "Field should not exist after deletion");
|
||||
print("✓ Field was successfully deleted from hash");
|
||||
|
||||
// Test redis_list operations
|
||||
print("Testing redis list operations...");
|
||||
let list_key = prefix + "list";
|
||||
|
||||
// Push items to list
|
||||
let rpush_result = redis_rpush(list_key, "item1");
|
||||
assert_true(rpush_result > 0, "RPUSH operation should succeed");
|
||||
redis_rpush(list_key, "item2");
|
||||
redis_rpush(list_key, "item3");
|
||||
print(`✓ redis_rpush(): Successfully pushed items to list ${list_key}`);
|
||||
|
||||
// Get list length
|
||||
let llen_result = redis_llen(list_key);
|
||||
assert_true(llen_result == 3, "List should have 3 items");
|
||||
print(`✓ redis_llen(): List has ${llen_result} items`);
|
||||
|
||||
// Get list range
|
||||
let lrange_result = redis_lrange(list_key, 0, -1);
|
||||
assert_true(lrange_result.len() == 3, "LRANGE should return 3 items");
|
||||
assert_true(lrange_result[0] == "item1", "First item should be 'item1'");
|
||||
assert_true(lrange_result[2] == "item3", "Last item should be 'item3'");
|
||||
print(`✓ redis_lrange(): Successfully retrieved all items from list ${list_key}`);
|
||||
|
||||
// Clean up
|
||||
print("Cleaning up...");
|
||||
redis_del(hash_key);
|
||||
redis_del(list_key);
|
||||
print("✓ Cleanup: All test keys removed");
|
||||
|
||||
print("All Redis operations tests completed successfully!");
|
@@ -1,59 +0,0 @@
|
||||
// 03_redis_authentication.rhai
|
||||
// Tests for Redis client authentication (placeholder for future implementation)
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if Redis is available
|
||||
fn is_redis_available() {
|
||||
try {
|
||||
// Try to execute a simple ping
|
||||
let ping_result = redis_ping();
|
||||
return ping_result == "PONG";
|
||||
} catch(err) {
|
||||
print(`Redis connection error: ${err}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Redis Client Authentication ===");
|
||||
|
||||
// Check if Redis is available
|
||||
let redis_available = is_redis_available();
|
||||
if !redis_available {
|
||||
print("Redis server is not available. Skipping Redis authentication tests.");
|
||||
// Exit gracefully without error
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ Redis server is available");
|
||||
|
||||
print("Authentication support will be implemented in a future update.");
|
||||
print("The backend implementation is ready, but the Rhai bindings are still in development.");
|
||||
|
||||
// For now, just test basic Redis functionality
|
||||
print("\nTesting basic Redis functionality...");
|
||||
|
||||
// Test a simple operation
|
||||
let test_key = "auth_test_key";
|
||||
let test_value = "auth_test_value";
|
||||
|
||||
let set_result = redis_set(test_key, test_value);
|
||||
assert_true(set_result, "Should be able to set a key");
|
||||
print("✓ Set key");
|
||||
|
||||
let get_result = redis_get(test_key);
|
||||
assert_true(get_result == test_value, "Should be able to get the key");
|
||||
print("✓ Got key");
|
||||
|
||||
// Clean up
|
||||
let del_result = redis_del(test_key);
|
||||
assert_true(del_result, "Should be able to delete the key");
|
||||
print("✓ Deleted test key");
|
||||
|
||||
print("All Redis tests completed successfully!");
|
@@ -1,154 +0,0 @@
|
||||
// run_all_tests.rhai
|
||||
// Runs all Redis client module tests
|
||||
|
||||
print("=== Running Redis Client Module Tests ===");
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if Redis is available
|
||||
fn is_redis_available() {
|
||||
try {
|
||||
// Try to execute a simple PING command
|
||||
let ping_result = redis_ping();
|
||||
return ping_result == "PONG";
|
||||
} catch(err) {
|
||||
print(`Redis connection error: ${err}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Run each test directly
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
let skipped = 0;
|
||||
|
||||
// Check if Redis is available
|
||||
let redis_available = is_redis_available();
|
||||
if !redis_available {
|
||||
print("Redis server is not available. Skipping all Redis tests.");
|
||||
skipped = 3; // Skip all three tests
|
||||
} else {
|
||||
// Test 1: Redis Connection
|
||||
print("\n--- Running Redis Connection Tests ---");
|
||||
try {
|
||||
// Test redis_ping function
|
||||
print("Testing redis_ping()...");
|
||||
let ping_result = redis_ping();
|
||||
assert_true(ping_result == "PONG", "PING should return PONG");
|
||||
print(`✓ redis_ping(): Returned ${ping_result}`);
|
||||
|
||||
// Test redis_set and redis_get functions
|
||||
print("Testing redis_set() and redis_get()...");
|
||||
let test_key = "rhai_test_key";
|
||||
let test_value = "Hello from Rhai test";
|
||||
|
||||
// Set a value
|
||||
let set_result = redis_set(test_key, test_value);
|
||||
assert_true(set_result, "SET operation should succeed");
|
||||
print(`✓ redis_set(): Successfully set key ${test_key}`);
|
||||
|
||||
// Get the value back
|
||||
let get_result = redis_get(test_key);
|
||||
assert_true(get_result == test_value, "GET should return the value we set");
|
||||
print(`✓ redis_get(): Successfully retrieved value for key ${test_key}`);
|
||||
|
||||
// Clean up
|
||||
redis_del(test_key);
|
||||
|
||||
print("--- Redis Connection Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Redis Connection Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
|
||||
// Test 2: Redis Operations
|
||||
print("\n--- Running Redis Operations Tests ---");
|
||||
try {
|
||||
// Test prefix for all keys to avoid conflicts
|
||||
let prefix = "rhai_test_";
|
||||
|
||||
// Test redis_hset and redis_hget functions
|
||||
print("Testing redis_hset() and redis_hget()...");
|
||||
let hash_key = prefix + "hash";
|
||||
let field = "field1";
|
||||
let value = "value1";
|
||||
|
||||
// Set hash field
|
||||
let hset_result = redis_hset(hash_key, field, value);
|
||||
assert_true(hset_result, "HSET operation should succeed");
|
||||
print(`✓ redis_hset(): Successfully set field in hash ${hash_key}`);
|
||||
|
||||
// Get hash field
|
||||
let hget_result = redis_hget(hash_key, field);
|
||||
assert_true(hget_result == value, "HGET should return the value we set");
|
||||
print(`✓ redis_hget(): Successfully retrieved value from hash ${hash_key}`);
|
||||
|
||||
// Clean up
|
||||
redis_del(hash_key);
|
||||
|
||||
print("--- Redis Operations Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Redis Operations Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
|
||||
// Test 3: Redis Authentication
|
||||
print("\n--- Running Redis Authentication Tests ---");
|
||||
try {
|
||||
print("Authentication support will be implemented in a future update.");
|
||||
print("The backend implementation is ready, but the Rhai bindings are still in development.");
|
||||
|
||||
// For now, just test basic Redis functionality
|
||||
print("\nTesting basic Redis functionality...");
|
||||
|
||||
// Test a simple operation
|
||||
let test_key = "auth_test_key";
|
||||
let test_value = "auth_test_value";
|
||||
|
||||
let set_result = redis_set(test_key, test_value);
|
||||
assert_true(set_result, "Should be able to set a key");
|
||||
print("✓ Set key");
|
||||
|
||||
let get_result = redis_get(test_key);
|
||||
assert_true(get_result == test_value, "Should be able to get the key");
|
||||
print("✓ Got key");
|
||||
|
||||
// Clean up
|
||||
let del_result = redis_del(test_key);
|
||||
assert_true(del_result, "Should be able to delete the key");
|
||||
print("✓ Deleted test key");
|
||||
|
||||
print("--- Redis Authentication Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Redis Authentication Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
print("\n=== Test Summary ===");
|
||||
print(`Passed: ${passed}`);
|
||||
print(`Failed: ${failed}`);
|
||||
print(`Skipped: ${skipped}`);
|
||||
print(`Total: ${passed + failed + skipped}`);
|
||||
|
||||
if failed == 0 {
|
||||
if skipped > 0 {
|
||||
print("\n⚠️ All tests skipped or passed!");
|
||||
} else {
|
||||
print("\n✅ All tests passed!");
|
||||
}
|
||||
} else {
|
||||
print("\n❌ Some tests failed!");
|
||||
}
|
||||
|
||||
// Return the number of failed tests (0 means success)
|
||||
failed;
|
@@ -1,152 +0,0 @@
|
||||
// 01_mount_operations.rhai
|
||||
// Tests for RFS mount operations
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if rfs is available
|
||||
fn is_rfs_available() {
|
||||
try {
|
||||
let result = run("which rfs");
|
||||
return result.success;
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to clean up mounts
|
||||
fn cleanup_mounts() {
|
||||
try {
|
||||
rfs_unmount_all();
|
||||
print("All mounts cleaned up");
|
||||
} catch(err) {
|
||||
print(`Error cleaning up mounts: ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing RFS Mount Operations ===");
|
||||
|
||||
// Check if rfs is available
|
||||
let rfs_available = is_rfs_available();
|
||||
if !rfs_available {
|
||||
print("rfs is not available. Skipping RFS tests.");
|
||||
// Exit gracefully without error
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ rfs is available");
|
||||
|
||||
// Clean up any existing mounts
|
||||
cleanup_mounts();
|
||||
|
||||
// Create test directories
|
||||
let source_dir = "rhai_test_rfs_source";
|
||||
let target_dir = "rhai_test_rfs_target";
|
||||
mkdir(source_dir);
|
||||
mkdir(target_dir);
|
||||
|
||||
// Create a test file in the source directory
|
||||
let test_file = `${source_dir}/test.txt`;
|
||||
file_write(test_file, "Hello from RFS test");
|
||||
|
||||
try {
|
||||
// Test mounting a local directory
|
||||
print("Testing rfs_mount() with local directory...");
|
||||
let options = #{
|
||||
"readonly": "true"
|
||||
};
|
||||
|
||||
let mount = rfs_mount(source_dir, target_dir, "local", options);
|
||||
|
||||
// Verify mount properties
|
||||
assert_true(mount.id != "", "Mount ID should not be empty");
|
||||
assert_eq(mount.source, source_dir, "Mount source should match");
|
||||
assert_eq(mount.target, target_dir, "Mount target should match");
|
||||
assert_eq(mount.fs_type, "local", "Mount type should be local");
|
||||
print(`✓ rfs_mount(): Mounted ${mount.source} to ${mount.target} with ID: ${mount.id}`);
|
||||
|
||||
// Test listing mounts
|
||||
print("Testing rfs_list_mounts()...");
|
||||
let mounts = rfs_list_mounts();
|
||||
assert_true(mounts.len() > 0, "There should be at least one mount");
|
||||
|
||||
// Find our mount in the list
|
||||
let found = false;
|
||||
for m in mounts {
|
||||
if m.target == target_dir {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert_true(found, "Our mount should be in the list");
|
||||
print(`✓ rfs_list_mounts(): Found ${mounts.len()} mounts`);
|
||||
|
||||
// Test getting mount info
|
||||
print("Testing rfs_get_mount_info()...");
|
||||
let mount_info = rfs_get_mount_info(target_dir);
|
||||
assert_eq(mount_info.target, target_dir, "Mount info target should match");
|
||||
assert_eq(mount_info.source, source_dir, "Mount info source should match");
|
||||
print(`✓ rfs_get_mount_info(): Got info for mount at ${mount_info.target}`);
|
||||
|
||||
// Verify the mounted file is accessible
|
||||
let mounted_file = `${target_dir}/test.txt`;
|
||||
assert_true(exist(mounted_file), "Mounted file should exist");
|
||||
let mounted_content = file_read(mounted_file);
|
||||
assert_eq(mounted_content, "Hello from RFS test", "Mounted file content should match");
|
||||
print("✓ Mounted file is accessible and content matches");
|
||||
|
||||
// Test unmounting a specific mount
|
||||
print("Testing rfs_unmount()...");
|
||||
rfs_unmount(target_dir);
|
||||
|
||||
// Verify the mount is gone
|
||||
try {
|
||||
rfs_get_mount_info(target_dir);
|
||||
assert_true(false, "Mount should not exist after unmounting");
|
||||
} catch(err) {
|
||||
print("✓ rfs_unmount(): Mount successfully unmounted");
|
||||
}
|
||||
|
||||
// Mount again to test unmount_all
|
||||
print("Testing mounting again for unmount_all...");
|
||||
let mount2 = rfs_mount(source_dir, target_dir, "local", options);
|
||||
assert_true(mount2.id != "", "Mount ID should not be empty");
|
||||
|
||||
// Test unmounting all mounts
|
||||
print("Testing rfs_unmount_all()...");
|
||||
rfs_unmount_all();
|
||||
|
||||
// Verify all mounts are gone
|
||||
let mounts_after = rfs_list_mounts();
|
||||
assert_true(mounts_after.len() == 0, "There should be no mounts after unmount_all");
|
||||
print("✓ rfs_unmount_all(): All mounts successfully unmounted");
|
||||
|
||||
print("All mount operations tests completed successfully!");
|
||||
} catch(err) {
|
||||
print(`Error: ${err}`);
|
||||
|
||||
// Clean up in case of error
|
||||
cleanup_mounts();
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
// Clean up test directories
|
||||
delete(source_dir);
|
||||
delete(target_dir);
|
||||
print("✓ Cleanup: Test directories removed");
|
||||
}
|
@@ -1,117 +0,0 @@
|
||||
// 02_filesystem_layer_operations.rhai
|
||||
// Tests for RFS filesystem layer operations
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if rfs is available
|
||||
fn is_rfs_available() {
|
||||
try {
|
||||
let result = run("which rfs");
|
||||
return result.success;
|
||||
} catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing RFS Filesystem Layer Operations ===");
|
||||
|
||||
// Check if rfs is available
|
||||
let rfs_available = is_rfs_available();
|
||||
if !rfs_available {
|
||||
print("rfs is not available. Skipping RFS tests.");
|
||||
// Exit gracefully without error
|
||||
return;
|
||||
}
|
||||
|
||||
print("✓ rfs is available");
|
||||
|
||||
// Create test directories
|
||||
let source_dir = "rhai_test_rfs_source";
|
||||
let unpack_dir = "rhai_test_rfs_unpack";
|
||||
mkdir(source_dir);
|
||||
mkdir(unpack_dir);
|
||||
|
||||
// Create test files in the source directory
|
||||
file_write(`${source_dir}/file1.txt`, "Content of file 1");
|
||||
file_write(`${source_dir}/file2.txt`, "Content of file 2");
|
||||
|
||||
// Create a subdirectory with files
|
||||
mkdir(`${source_dir}/subdir`);
|
||||
file_write(`${source_dir}/subdir/file3.txt`, "Content of file 3");
|
||||
|
||||
// Output file for the filesystem layer
|
||||
let output_file = "rhai_test_rfs_layer.fl";
|
||||
|
||||
try {
|
||||
// Test packing a directory
|
||||
print("Testing rfs_pack()...");
|
||||
// Use a file store spec for testing
|
||||
let store_specs = "file:path=.";
|
||||
rfs_pack(source_dir, output_file, store_specs);
|
||||
|
||||
// Verify the output file exists
|
||||
assert_true(exist(output_file), "Output file should exist");
|
||||
print(`✓ rfs_pack(): Directory packed to ${output_file}`);
|
||||
|
||||
// Test listing contents of the filesystem layer
|
||||
print("Testing rfs_list_contents()...");
|
||||
let contents = rfs_list_contents(output_file);
|
||||
|
||||
// Verify the contents include our files
|
||||
assert_true(contents.contains("file1.txt"), "Contents should include file1.txt");
|
||||
assert_true(contents.contains("file2.txt"), "Contents should include file2.txt");
|
||||
assert_true(contents.contains("subdir/file3.txt"), "Contents should include subdir/file3.txt");
|
||||
print("✓ rfs_list_contents(): Layer contents listed successfully");
|
||||
|
||||
// Test verifying the filesystem layer
|
||||
print("Testing rfs_verify()...");
|
||||
let is_valid = rfs_verify(output_file);
|
||||
assert_true(is_valid, "Filesystem layer should be valid");
|
||||
print("✓ rfs_verify(): Layer verified successfully");
|
||||
|
||||
// Test unpacking the filesystem layer
|
||||
print("Testing rfs_unpack()...");
|
||||
rfs_unpack(output_file, unpack_dir);
|
||||
|
||||
// Verify the unpacked files exist and have the correct content
|
||||
assert_true(exist(`${unpack_dir}/file1.txt`), "Unpacked file1.txt should exist");
|
||||
assert_true(exist(`${unpack_dir}/file2.txt`), "Unpacked file2.txt should exist");
|
||||
assert_true(exist(`${unpack_dir}/subdir/file3.txt`), "Unpacked subdir/file3.txt should exist");
|
||||
|
||||
let content1 = file_read(`${unpack_dir}/file1.txt`);
|
||||
let content2 = file_read(`${unpack_dir}/file2.txt`);
|
||||
let content3 = file_read(`${unpack_dir}/subdir/file3.txt`);
|
||||
|
||||
assert_eq(content1, "Content of file 1", "Content of file1.txt should match");
|
||||
assert_eq(content2, "Content of file 2", "Content of file2.txt should match");
|
||||
assert_eq(content3, "Content of file 3", "Content of file3.txt should match");
|
||||
|
||||
print("✓ rfs_unpack(): Layer unpacked successfully");
|
||||
|
||||
print("All filesystem layer operations tests completed successfully!");
|
||||
} catch(err) {
|
||||
print(`Error: ${err}`);
|
||||
throw err;
|
||||
} finally {
|
||||
// Clean up test directories and files
|
||||
delete(source_dir);
|
||||
delete(unpack_dir);
|
||||
delete(output_file);
|
||||
print("✓ Cleanup: Test directories and files removed");
|
||||
}
|
@@ -1,168 +0,0 @@
|
||||
// run_all_tests.rhai
|
||||
// Runs all RFS module tests
|
||||
|
||||
print("=== Running RFS Module Tests ===");
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to check if rfs is available
|
||||
fn is_rfs_available() {
|
||||
try {
|
||||
let result = run("which rfs");
|
||||
return result.success;
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to clean up mounts
|
||||
fn cleanup_mounts() {
|
||||
try {
|
||||
rfs_unmount_all();
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
|
||||
// Run each test directly
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
let skipped = 0;
|
||||
let total = 0;
|
||||
|
||||
// Check if rfs is available
|
||||
let rfs_available = is_rfs_available();
|
||||
if !rfs_available {
|
||||
print("rfs is not available. Skipping all RFS tests.");
|
||||
skipped = 2; // Skip both tests
|
||||
total = 2;
|
||||
} else {
|
||||
// Test 1: Mount Operations
|
||||
print("\n--- Running Mount Operations Tests ---");
|
||||
try {
|
||||
// Clean up any existing mounts
|
||||
cleanup_mounts();
|
||||
|
||||
// Create test directories
|
||||
let source_dir = "rhai_test_rfs_source";
|
||||
let target_dir = "rhai_test_rfs_target";
|
||||
mkdir(source_dir);
|
||||
mkdir(target_dir);
|
||||
|
||||
// Create a test file in the source directory
|
||||
let test_file = `${source_dir}/test.txt`;
|
||||
file_write(test_file, "Hello from RFS test");
|
||||
|
||||
// Mount the directory
|
||||
let options = #{
|
||||
"readonly": "true"
|
||||
};
|
||||
|
||||
let mount = rfs_mount(source_dir, target_dir, "local", options);
|
||||
assert_true(mount.id != "", "Mount ID should not be empty");
|
||||
|
||||
// List mounts
|
||||
let mounts = rfs_list_mounts();
|
||||
assert_true(mounts.len() > 0, "There should be at least one mount");
|
||||
|
||||
// Unmount
|
||||
rfs_unmount(target_dir);
|
||||
|
||||
// Clean up
|
||||
delete(source_dir);
|
||||
delete(target_dir);
|
||||
|
||||
print("--- Mount Operations Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Mount Operations Tests: ${err}`);
|
||||
failed += 1;
|
||||
|
||||
// Clean up in case of error
|
||||
cleanup_mounts();
|
||||
try {
|
||||
delete("rhai_test_rfs_source");
|
||||
delete("rhai_test_rfs_target");
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
total += 1;
|
||||
|
||||
// Test 2: Filesystem Layer Operations
|
||||
print("\n--- Running Filesystem Layer Operations Tests ---");
|
||||
try {
|
||||
// Create test directories
|
||||
let source_dir = "rhai_test_rfs_source";
|
||||
let unpack_dir = "rhai_test_rfs_unpack";
|
||||
mkdir(source_dir);
|
||||
mkdir(unpack_dir);
|
||||
|
||||
// Create test files in the source directory
|
||||
file_write(`${source_dir}/file1.txt`, "Content of file 1");
|
||||
|
||||
// Output file for the filesystem layer
|
||||
let output_file = "rhai_test_rfs_layer.fl";
|
||||
|
||||
// Pack the directory
|
||||
let store_specs = "file:path=.";
|
||||
rfs_pack(source_dir, output_file, store_specs);
|
||||
|
||||
// List contents
|
||||
let contents = rfs_list_contents(output_file);
|
||||
assert_true(contents.contains("file1.txt"), "Contents should include file1.txt");
|
||||
|
||||
// Verify the layer
|
||||
let is_valid = rfs_verify(output_file);
|
||||
assert_true(is_valid, "Filesystem layer should be valid");
|
||||
|
||||
// Unpack the layer
|
||||
rfs_unpack(output_file, unpack_dir);
|
||||
|
||||
// Clean up
|
||||
delete(source_dir);
|
||||
delete(unpack_dir);
|
||||
delete(output_file);
|
||||
|
||||
print("--- Filesystem Layer Operations Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Filesystem Layer Operations Tests: ${err}`);
|
||||
failed += 1;
|
||||
|
||||
// Clean up in case of error
|
||||
try {
|
||||
delete("rhai_test_rfs_source");
|
||||
delete("rhai_test_rfs_unpack");
|
||||
delete("rhai_test_rfs_layer.fl");
|
||||
} catch(e) {
|
||||
// Ignore errors during cleanup
|
||||
}
|
||||
}
|
||||
total += 1;
|
||||
}
|
||||
|
||||
print("\n=== Test Summary ===");
|
||||
print(`Passed: ${passed}`);
|
||||
print(`Failed: ${failed}`);
|
||||
print(`Skipped: ${skipped}`);
|
||||
print(`Total: ${total}`);
|
||||
|
||||
if failed == 0 {
|
||||
if skipped > 0 {
|
||||
print("\n⚠️ All tests skipped or passed!");
|
||||
} else {
|
||||
print("\n✅ All tests passed!");
|
||||
}
|
||||
} else {
|
||||
print("\n❌ Some tests failed!");
|
||||
}
|
||||
|
||||
// Return the number of failed tests (0 means success)
|
||||
failed;
|
@@ -1,95 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Run all Rhai tests
|
||||
# This script runs all the Rhai tests in the rhai_tests directory
|
||||
|
||||
# Set the base directory
|
||||
BASE_DIR="src/rhai_tests"
|
||||
|
||||
# Define colors for output
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[0;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Initialize counters
|
||||
TOTAL_MODULES=0
|
||||
PASSED_MODULES=0
|
||||
FAILED_MODULES=0
|
||||
|
||||
# Function to run tests in a directory
|
||||
run_tests_in_dir() {
|
||||
local dir=$1
|
||||
local module_name=$(basename $dir)
|
||||
|
||||
echo -e "${YELLOW}Running tests for module: ${module_name}${NC}"
|
||||
|
||||
# Check if the directory has a run_all_tests.rhai script
|
||||
if [ -f "${dir}/run_all_tests.rhai" ]; then
|
||||
echo "Using module's run_all_tests.rhai script"
|
||||
herodo --path "${dir}/run_all_tests.rhai"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ All tests passed for module: ${module_name}${NC}"
|
||||
PASSED_MODULES=$((PASSED_MODULES + 1))
|
||||
else
|
||||
echo -e "${RED}✗ Tests failed for module: ${module_name}${NC}"
|
||||
FAILED_MODULES=$((FAILED_MODULES + 1))
|
||||
fi
|
||||
else
|
||||
# Run all .rhai files in the directory
|
||||
local test_files=$(find "${dir}" -name "*.rhai" | sort)
|
||||
local all_passed=true
|
||||
|
||||
for test_file in $test_files; do
|
||||
echo "Running test: $(basename $test_file)"
|
||||
herodo --path "$test_file"
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
all_passed=false
|
||||
fi
|
||||
done
|
||||
|
||||
if $all_passed; then
|
||||
echo -e "${GREEN}✓ All tests passed for module: ${module_name}${NC}"
|
||||
PASSED_MODULES=$((PASSED_MODULES + 1))
|
||||
else
|
||||
echo -e "${RED}✗ Tests failed for module: ${module_name}${NC}"
|
||||
FAILED_MODULES=$((FAILED_MODULES + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
TOTAL_MODULES=$((TOTAL_MODULES + 1))
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
echo "=======================================
|
||||
Running Rhai Tests
|
||||
======================================="
|
||||
|
||||
# Find all module directories
|
||||
for dir in $(find "${BASE_DIR}" -mindepth 1 -maxdepth 1 -type d | sort); do
|
||||
run_tests_in_dir "$dir"
|
||||
done
|
||||
|
||||
# Print summary
|
||||
echo "=======================================
|
||||
Test Summary
|
||||
======================================="
|
||||
echo "Total modules tested: ${TOTAL_MODULES}"
|
||||
echo "Passed: ${PASSED_MODULES}"
|
||||
echo "Failed: ${FAILED_MODULES}"
|
||||
|
||||
if [ $FAILED_MODULES -gt 0 ]; then
|
||||
echo -e "${RED}Some tests failed!${NC}"
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}All tests passed!${NC}"
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Run the main function
|
||||
main
|
@@ -1,108 +0,0 @@
|
||||
// 01_text_indentation.rhai
|
||||
// Tests for text indentation functions in the Text module
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Text Indentation Functions ===");
|
||||
|
||||
// Test dedent function
|
||||
print("Testing dedent()...");
|
||||
|
||||
// Test case 1: Basic indentation
|
||||
let indented_text1 = " line 1\n line 2\n line 3";
|
||||
let expected_dedented1 = "line 1\nline 2\n line 3";
|
||||
let dedented1 = dedent(indented_text1);
|
||||
assert_eq(dedented1, expected_dedented1, "Basic indentation should be removed correctly");
|
||||
print("✓ dedent(): Basic indentation removed correctly");
|
||||
|
||||
// Test case 2: Mixed indentation
|
||||
let indented_text2 = " line 1\n line 2\n line 3";
|
||||
let expected_dedented2 = "line 1\n line 2\nline 3";
|
||||
let dedented2 = dedent(indented_text2);
|
||||
assert_eq(dedented2, expected_dedented2, "Mixed indentation should be handled correctly");
|
||||
print("✓ dedent(): Mixed indentation handled correctly");
|
||||
|
||||
// Test case 3: Empty lines
|
||||
let indented_text3 = " line 1\n\n line 3";
|
||||
let expected_dedented3 = "line 1\n\nline 3";
|
||||
let dedented3 = dedent(indented_text3);
|
||||
assert_eq(dedented3, expected_dedented3, "Empty lines should be preserved");
|
||||
print("✓ dedent(): Empty lines preserved correctly");
|
||||
|
||||
// Test case 4: No indentation
|
||||
let text4 = "line 1\nline 2\nline 3";
|
||||
let dedented4 = dedent(text4);
|
||||
assert_eq(dedented4, text4, "Text without indentation should remain unchanged");
|
||||
print("✓ dedent(): Text without indentation remains unchanged");
|
||||
|
||||
// Test case 5: Single line
|
||||
let indented_text5 = " single line";
|
||||
let expected_dedented5 = "single line";
|
||||
let dedented5 = dedent(indented_text5);
|
||||
assert_eq(dedented5, expected_dedented5, "Single line indentation should be removed");
|
||||
print("✓ dedent(): Single line indentation removed correctly");
|
||||
|
||||
// Test prefix function
|
||||
print("\nTesting prefix()...");
|
||||
|
||||
// Test case 1: Basic prefix
|
||||
let text1 = "line 1\nline 2\nline 3";
|
||||
let expected_prefixed1 = " line 1\n line 2\n line 3";
|
||||
let prefixed1 = prefix(text1, " ");
|
||||
assert_eq(prefixed1, expected_prefixed1, "Basic prefix should be added correctly");
|
||||
print("✓ prefix(): Basic prefix added correctly");
|
||||
|
||||
// Test case 2: Empty prefix
|
||||
let text2 = "line 1\nline 2\nline 3";
|
||||
let prefixed2 = prefix(text2, "");
|
||||
assert_eq(prefixed2, text2, "Empty prefix should not change the text");
|
||||
print("✓ prefix(): Empty prefix doesn't change the text");
|
||||
|
||||
// Test case 3: Prefix with empty lines
|
||||
let text3 = "line 1\n\nline 3";
|
||||
let expected_prefixed3 = " line 1\n \n line 3";
|
||||
let prefixed3 = prefix(text3, " ");
|
||||
assert_eq(prefixed3, expected_prefixed3, "Prefix should be added to empty lines");
|
||||
print("✓ prefix(): Prefix added to empty lines correctly");
|
||||
|
||||
// Test case 4: Single line
|
||||
let text4 = "single line";
|
||||
let expected_prefixed4 = " single line";
|
||||
let prefixed4 = prefix(text4, " ");
|
||||
assert_eq(prefixed4, expected_prefixed4, "Prefix should be added to single line");
|
||||
print("✓ prefix(): Prefix added to single line correctly");
|
||||
|
||||
// Test case 5: Non-space prefix
|
||||
let text5 = "line 1\nline 2\nline 3";
|
||||
let expected_prefixed5 = ">>> line 1\n>>> line 2\n>>> line 3";
|
||||
let prefixed5 = prefix(text5, ">>> ");
|
||||
assert_eq(prefixed5, expected_prefixed5, "Non-space prefix should be added correctly");
|
||||
print("✓ prefix(): Non-space prefix added correctly");
|
||||
|
||||
// Test combining dedent and prefix
|
||||
print("\nTesting combination of dedent() and prefix()...");
|
||||
|
||||
let indented_text = " line 1\n line 2\n line 3";
|
||||
let dedented = dedent(indented_text);
|
||||
let prefixed = prefix(dedented, " ");
|
||||
let expected_result = " line 1\n line 2\n line 3";
|
||||
assert_eq(prefixed, expected_result, "Combination of dedent and prefix should work correctly");
|
||||
print("✓ dedent() + prefix(): Combination works correctly");
|
||||
|
||||
print("\nAll text indentation tests completed successfully!");
|
@@ -1,100 +0,0 @@
|
||||
// 02_name_path_fix.rhai
|
||||
// Tests for filename and path normalization functions in the Text module
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Filename and Path Normalization Functions ===");
|
||||
|
||||
// Test name_fix function
|
||||
print("Testing name_fix()...");
|
||||
|
||||
// Test case 1: Basic name fixing
|
||||
let name1 = "Hello World";
|
||||
let expected_fixed1 = "hello_world";
|
||||
let fixed1 = name_fix(name1);
|
||||
assert_eq(fixed1, expected_fixed1, "Spaces should be replaced with underscores and converted to lowercase");
|
||||
print("✓ name_fix(): Basic name fixing works correctly");
|
||||
|
||||
// Test case 2: Special characters
|
||||
let name2 = "File-Name.txt";
|
||||
let expected_fixed2 = "file_name.txt";
|
||||
let fixed2 = name_fix(name2);
|
||||
assert_eq(fixed2, expected_fixed2, "Hyphens should be replaced with underscores");
|
||||
print("✓ name_fix(): Special characters handled correctly");
|
||||
|
||||
// Test case 3: Multiple special characters
|
||||
let name3 = "Test!@#$%^&*()";
|
||||
let expected_fixed3 = "test_";
|
||||
let fixed3 = name_fix(name3);
|
||||
assert_eq(fixed3, expected_fixed3, "Multiple special characters should be collapsed into a single underscore");
|
||||
print("✓ name_fix(): Multiple special characters handled correctly");
|
||||
|
||||
// Test case 4: Non-ASCII characters
|
||||
let name4 = "Café";
|
||||
let expected_fixed4 = "caf";
|
||||
let fixed4 = name_fix(name4);
|
||||
assert_eq(fixed4, expected_fixed4, "Non-ASCII characters should be removed");
|
||||
print("✓ name_fix(): Non-ASCII characters removed correctly");
|
||||
|
||||
// Test case 5: Uppercase conversion
|
||||
let name5 = "UPPERCASE";
|
||||
let expected_fixed5 = "uppercase";
|
||||
let fixed5 = name_fix(name5);
|
||||
assert_eq(fixed5, expected_fixed5, "Uppercase should be converted to lowercase");
|
||||
print("✓ name_fix(): Uppercase conversion works correctly");
|
||||
|
||||
// Test path_fix function
|
||||
print("\nTesting path_fix()...");
|
||||
|
||||
// Test case 1: Path ending with /
|
||||
let path1 = "/path/to/directory/";
|
||||
let expected_fixed_path1 = "/path/to/directory/";
|
||||
let fixed_path1 = path_fix(path1);
|
||||
assert_eq(fixed_path1, expected_fixed_path1, "Path ending with / should remain unchanged");
|
||||
print("✓ path_fix(): Path ending with / remains unchanged");
|
||||
|
||||
// Test case 2: Single filename
|
||||
let path2 = "filename.txt";
|
||||
let expected_fixed_path2 = "filename.txt";
|
||||
let fixed_path2 = path_fix(path2);
|
||||
assert_eq(fixed_path2, expected_fixed_path2, "Single filename should be fixed");
|
||||
print("✓ path_fix(): Single filename fixed correctly");
|
||||
|
||||
// Test case 3: Path with filename
|
||||
let path3 = "/path/to/File Name.txt";
|
||||
let expected_fixed_path3 = "/path/to/file_name.txt";
|
||||
let fixed_path3 = path_fix(path3);
|
||||
assert_eq(fixed_path3, expected_fixed_path3, "Only the filename part of the path should be fixed");
|
||||
print("✓ path_fix(): Path with filename fixed correctly");
|
||||
|
||||
// Test case 4: Relative path
|
||||
let path4 = "./relative/path/to/DOCUMENT-123.pdf";
|
||||
let expected_fixed_path4 = "./relative/path/to/document_123.pdf";
|
||||
let fixed_path4 = path_fix(path4);
|
||||
assert_eq(fixed_path4, expected_fixed_path4, "Relative path should be handled correctly");
|
||||
print("✓ path_fix(): Relative path handled correctly");
|
||||
|
||||
// Test case 5: Path with special characters in filename
|
||||
let path5 = "/path/with/[special]<chars>.txt";
|
||||
let expected_fixed_path5 = "/path/with/_special_chars_.txt";
|
||||
let fixed_path5 = path_fix(path5);
|
||||
assert_eq(fixed_path5, expected_fixed_path5, "Special characters in filename should be handled correctly");
|
||||
print("✓ path_fix(): Path with special characters in filename handled correctly");
|
||||
|
||||
print("\nAll filename and path normalization tests completed successfully!");
|
@@ -1,134 +0,0 @@
|
||||
// 03_text_replacer.rhai
|
||||
// Tests for text replacement functions in the Text module
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Text Replacement Functions ===");
|
||||
|
||||
// Test TextReplacer with simple replacements
|
||||
print("Testing TextReplacer with simple replacements...");
|
||||
|
||||
// Test case 1: Basic replacement
|
||||
let replacer1 = text_replacer_new()
|
||||
.pattern("foo")
|
||||
.replacement("bar")
|
||||
.build();
|
||||
|
||||
let input1 = "foo bar foo";
|
||||
let expected_output1 = "bar bar bar";
|
||||
let output1 = replacer1.replace(input1);
|
||||
assert_eq(output1, expected_output1, "Basic replacement should work correctly");
|
||||
print("✓ TextReplacer: Basic replacement works correctly");
|
||||
|
||||
// Test case 2: Multiple replacements
|
||||
let replacer2 = text_replacer_new()
|
||||
.pattern("foo")
|
||||
.replacement("bar")
|
||||
.and()
|
||||
.pattern("baz")
|
||||
.replacement("qux")
|
||||
.build();
|
||||
|
||||
let input2 = "foo baz foo";
|
||||
let expected_output2 = "bar qux bar";
|
||||
let output2 = replacer2.replace(input2);
|
||||
assert_eq(output2, expected_output2, "Multiple replacements should work correctly");
|
||||
print("✓ TextReplacer: Multiple replacements work correctly");
|
||||
|
||||
// Test TextReplacer with regex replacements
|
||||
print("\nTesting TextReplacer with regex replacements...");
|
||||
|
||||
// Test case 3: Basic regex replacement
|
||||
let replacer3 = text_replacer_new()
|
||||
.pattern("f.o")
|
||||
.replacement("bar")
|
||||
.regex(true)
|
||||
.build();
|
||||
|
||||
let input3 = "foo fao fio";
|
||||
let output3 = replacer3.replace(input3);
|
||||
// The regex "f.o" matches "foo", "fao", and "fio"
|
||||
let expected_output3 = "bar bar bar";
|
||||
assert_eq(output3, expected_output3, "Basic regex replacement should work correctly");
|
||||
print("✓ TextReplacer: Basic regex replacement works correctly");
|
||||
|
||||
// Test case 4: Case-insensitive regex replacement
|
||||
let replacer4 = text_replacer_new()
|
||||
.pattern("foo")
|
||||
.replacement("bar")
|
||||
.regex(true)
|
||||
.case_insensitive(true)
|
||||
.build();
|
||||
|
||||
let input4 = "FOO foo Foo";
|
||||
let expected_output4 = "bar bar bar";
|
||||
let output4 = replacer4.replace(input4);
|
||||
assert_eq(output4, expected_output4, "Case-insensitive regex replacement should work correctly");
|
||||
print("✓ TextReplacer: Case-insensitive regex replacement works correctly");
|
||||
|
||||
// Test TextReplacer with file operations
|
||||
print("\nTesting TextReplacer with file operations...");
|
||||
|
||||
// Create a temporary file for testing
|
||||
let test_dir = "rhai_test_text_replacer";
|
||||
mkdir(test_dir);
|
||||
let test_file = `${test_dir}/test_file.txt`;
|
||||
let test_output_file = `${test_dir}/test_output_file.txt`;
|
||||
|
||||
// Write test content to the file
|
||||
let test_content = "This is a test file with foo and bar.";
|
||||
file_write(test_file, test_content);
|
||||
|
||||
// Test case 5: Replace in file and get result as string
|
||||
let replacer5 = text_replacer_new()
|
||||
.pattern("foo")
|
||||
.replacement("baz")
|
||||
.build();
|
||||
|
||||
let expected_output5 = "This is a test file with baz and bar.";
|
||||
let output5 = replacer5.replace_file(test_file);
|
||||
assert_eq(output5, expected_output5, "replace_file should return the replaced content");
|
||||
print("✓ TextReplacer: replace_file works correctly");
|
||||
|
||||
// Test case 6: Replace in file and write to a new file
|
||||
replacer5.replace_file_to(test_file, test_output_file);
|
||||
let output_content = file_read(test_output_file);
|
||||
assert_eq(output_content, expected_output5, "replace_file_to should write the replaced content to a new file");
|
||||
print("✓ TextReplacer: replace_file_to works correctly");
|
||||
|
||||
// Test case 7: Replace in file and write back to the same file
|
||||
// First, update the test file with the replaced content
|
||||
file_write(test_file, expected_output5);
|
||||
|
||||
let replacer6 = text_replacer_new()
|
||||
.pattern("baz")
|
||||
.replacement("qux")
|
||||
.build();
|
||||
|
||||
replacer6.replace_file_in_place(test_file);
|
||||
let updated_content = file_read(test_file);
|
||||
let expected_output6 = "This is a test file with qux and bar.";
|
||||
assert_eq(updated_content, expected_output6, "replace_file_in_place should update the file in place");
|
||||
print("✓ TextReplacer: replace_file_in_place works correctly");
|
||||
|
||||
// Clean up
|
||||
delete(test_dir);
|
||||
print("✓ Cleanup: Test directory removed");
|
||||
|
||||
print("\nAll text replacement tests completed successfully!");
|
@@ -1,102 +0,0 @@
|
||||
// 04_template_builder.rhai
|
||||
// Tests for template rendering functions in the Text module
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
print("=== Testing Template Rendering Functions ===");
|
||||
|
||||
// Create a temporary directory for testing
|
||||
let test_dir = "rhai_test_template";
|
||||
mkdir(test_dir);
|
||||
|
||||
// Test TemplateBuilder with string template
|
||||
print("Testing TemplateBuilder with string template...");
|
||||
|
||||
// Test case 1: Basic template with string variable
|
||||
let template1 = "Hello, {{ name }}!";
|
||||
let builder1 = template_builder_open(template1);
|
||||
builder1.add_var("name", "World");
|
||||
let expected_output1 = "Hello, World!";
|
||||
let output1 = builder1.render();
|
||||
assert_eq(output1, expected_output1, "Basic template with string variable should render correctly");
|
||||
print("✓ TemplateBuilder: Basic template with string variable renders correctly");
|
||||
|
||||
// Test case 2: Template with multiple variables of different types
|
||||
let template2 = "{{ name }} is {{ age }} years old and {{ is_active ? 'active' : 'inactive' }}.";
|
||||
let builder2 = template_builder_open(template2);
|
||||
builder2.add_var("name", "John");
|
||||
builder2.add_var("age", 30);
|
||||
builder2.add_var("is_active", true);
|
||||
let expected_output2 = "John is 30 years old and active.";
|
||||
let output2 = builder2.render();
|
||||
assert_eq(output2, expected_output2, "Template with multiple variables should render correctly");
|
||||
print("✓ TemplateBuilder: Template with multiple variables renders correctly");
|
||||
|
||||
// Test case 3: Template with array variable
|
||||
let template3 = "Items: {% for item in items %}{{ item }}{% if !loop.last %}, {% endif %}{% endfor %}";
|
||||
let builder3 = template_builder_open(template3);
|
||||
let items = ["apple", "banana", "cherry"];
|
||||
builder3.add_var("items", items);
|
||||
let expected_output3 = "Items: apple, banana, cherry";
|
||||
let output3 = builder3.render();
|
||||
assert_eq(output3, expected_output3, "Template with array variable should render correctly");
|
||||
print("✓ TemplateBuilder: Template with array variable renders correctly");
|
||||
|
||||
// Test case 4: Template with map variable
|
||||
let template4 = "User: {{ user.name }}, Age: {{ user.age }}";
|
||||
let builder4 = template_builder_open(template4);
|
||||
let user = #{
|
||||
name: "Alice",
|
||||
age: 25
|
||||
};
|
||||
builder4.add_vars(user);
|
||||
let expected_output4 = "User: Alice, Age: 25";
|
||||
let output4 = builder4.render();
|
||||
assert_eq(output4, expected_output4, "Template with map variable should render correctly");
|
||||
print("✓ TemplateBuilder: Template with map variable renders correctly");
|
||||
|
||||
// Test TemplateBuilder with file operations
|
||||
print("\nTesting TemplateBuilder with file operations...");
|
||||
|
||||
// Create a template file
|
||||
let template_file = `${test_dir}/template.txt`;
|
||||
let template_content = "Hello, {{ name }}! You are {{ age }} years old.";
|
||||
file_write(template_file, template_content);
|
||||
|
||||
// Test case 5: Template from file
|
||||
let builder5 = template_builder_open(template_file);
|
||||
builder5.add_var("name", "Bob");
|
||||
builder5.add_var("age", 40);
|
||||
let expected_output5 = "Hello, Bob! You are 40 years old.";
|
||||
let output5 = builder5.render();
|
||||
assert_eq(output5, expected_output5, "Template from file should render correctly");
|
||||
print("✓ TemplateBuilder: Template from file renders correctly");
|
||||
|
||||
// Test case 6: Render to file
|
||||
let output_file = `${test_dir}/output.txt`;
|
||||
builder5.render_to_file(output_file);
|
||||
let output_content = file_read(output_file);
|
||||
assert_eq(output_content, expected_output5, "render_to_file should write the rendered content to a file");
|
||||
print("✓ TemplateBuilder: render_to_file works correctly");
|
||||
|
||||
// Clean up
|
||||
delete(test_dir);
|
||||
print("✓ Cleanup: Test directory removed");
|
||||
|
||||
print("\nAll template rendering tests completed successfully!");
|
@@ -1,138 +0,0 @@
|
||||
// run_all_tests.rhai
|
||||
// Runs all Text module tests
|
||||
|
||||
print("=== Running Text Module Tests ===");
|
||||
|
||||
// Custom assert function
|
||||
fn assert_true(condition, message) {
|
||||
if !condition {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom assert_eq function
|
||||
fn assert_eq(actual, expected, message) {
|
||||
if actual != expected {
|
||||
print(`ASSERTION FAILED: ${message}`);
|
||||
print(`Expected: "${expected}"`);
|
||||
print(`Actual: "${actual}"`);
|
||||
throw message;
|
||||
}
|
||||
}
|
||||
|
||||
// Run each test directly
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
let total = 0;
|
||||
|
||||
// Test 1: Text Indentation
|
||||
print("\n--- Running Text Indentation Tests ---");
|
||||
try {
|
||||
// Test dedent function
|
||||
print("Testing dedent()...");
|
||||
let indented_text = " line 1\n line 2\n line 3";
|
||||
let dedented = dedent(indented_text);
|
||||
assert_eq(dedented, "line 1\nline 2\n line 3", "Basic indentation should be removed correctly");
|
||||
print("✓ dedent(): Basic indentation removed correctly");
|
||||
|
||||
// Test prefix function
|
||||
print("Testing prefix()...");
|
||||
let text = "line 1\nline 2\nline 3";
|
||||
let prefixed = prefix(text, " ");
|
||||
assert_eq(prefixed, " line 1\n line 2\n line 3", "Basic prefix should be added correctly");
|
||||
print("✓ prefix(): Basic prefix added correctly");
|
||||
|
||||
print("--- Text Indentation Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Text Indentation Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
total += 1;
|
||||
|
||||
// Test 2: Filename and Path Normalization
|
||||
print("\n--- Running Filename and Path Normalization Tests ---");
|
||||
try {
|
||||
// Test name_fix function
|
||||
print("Testing name_fix()...");
|
||||
let name = "Hello World";
|
||||
let fixed_name = name_fix(name);
|
||||
assert_eq(fixed_name, "hello_world", "Spaces should be replaced with underscores and converted to lowercase");
|
||||
print("✓ name_fix(): Basic name fixing works correctly");
|
||||
|
||||
// Test path_fix function
|
||||
print("Testing path_fix()...");
|
||||
let path = "/path/to/File Name.txt";
|
||||
let fixed_path = path_fix(path);
|
||||
assert_eq(fixed_path, "/path/to/file_name.txt", "Only the filename part of the path should be fixed");
|
||||
print("✓ path_fix(): Path with filename fixed correctly");
|
||||
|
||||
print("--- Filename and Path Normalization Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Filename and Path Normalization Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
total += 1;
|
||||
|
||||
// Test 3: Text Replacement
|
||||
print("\n--- Running Text Replacement Tests ---");
|
||||
try {
|
||||
// Test TextReplacer with simple replacements
|
||||
print("Testing TextReplacer with simple replacements...");
|
||||
let replacer = text_replacer_new()
|
||||
.pattern("foo")
|
||||
.replacement("bar")
|
||||
.build();
|
||||
|
||||
let input = "foo bar foo";
|
||||
let output = replacer.replace(input);
|
||||
assert_eq(output, "bar bar bar", "Basic replacement should work correctly");
|
||||
print("✓ TextReplacer: Basic replacement works correctly");
|
||||
|
||||
// Create a temporary file for testing
|
||||
let test_dir = "rhai_test_text_replacer";
|
||||
mkdir(test_dir);
|
||||
let test_file = `${test_dir}/test_file.txt`;
|
||||
|
||||
// Write test content to the file
|
||||
let test_content = "This is a test file with foo and bar.";
|
||||
file_write(test_file, test_content);
|
||||
|
||||
// Test replace_file
|
||||
let expected_output = "This is a test file with bar and bar.";
|
||||
let output = replacer.replace_file(test_file);
|
||||
assert_eq(output, expected_output, "replace_file should return the replaced content");
|
||||
print("✓ TextReplacer: replace_file works correctly");
|
||||
|
||||
// Clean up
|
||||
delete(test_dir);
|
||||
print("✓ Cleanup: Test directory removed");
|
||||
|
||||
print("--- Text Replacement Tests completed successfully ---");
|
||||
passed += 1;
|
||||
} catch(err) {
|
||||
print(`!!! Error in Text Replacement Tests: ${err}`);
|
||||
failed += 1;
|
||||
}
|
||||
total += 1;
|
||||
|
||||
// Skip Template Rendering Tests for now
|
||||
print("\n--- Skipping Template Rendering Tests ---");
|
||||
print("Template rendering tests are skipped due to compatibility issues.");
|
||||
total += 1;
|
||||
|
||||
print("\n=== Test Summary ===");
|
||||
print(`Passed: ${passed}`);
|
||||
print(`Failed: ${failed}`);
|
||||
print(`Total: ${total}`);
|
||||
|
||||
if failed == 0 {
|
||||
print("\n✅ All tests passed!");
|
||||
} else {
|
||||
print("\n❌ Some tests failed!");
|
||||
}
|
||||
|
||||
// Return the number of failed tests (0 means success)
|
||||
failed;
|
Reference in New Issue
Block a user