wit 0.16.0

A tool for building and publishing WIT packages to a registry.
Documentation
use std::fs;

use anyhow::Result;
use assert_cmd::prelude::*;
use predicates::{prelude::*, str::contains};

use crate::support::*;

mod support;

#[test]
fn help() {
    for arg in ["help add", "add -h", "add --help"] {
        wit(arg.split_whitespace())
            .assert()
            .stdout(contains(
                "Adds a reference to a WIT package from a registry",
            ))
            .success();
    }
}

#[test]
fn it_fails_with_missing_toml_file() -> Result<()> {
    wit(["add", "foo:bar"])
        .assert()
        .stderr(contains(
            "error: failed to find configuration file `wit.toml`",
        ))
        .failure();
    Ok(())
}

#[test]
fn requires_package() {
    wit(["add"])
        .assert()
        .stderr(contains("wit add <PACKAGE>"))
        .failure();
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn validate_the_package_exists() -> Result<()> {
    let (server, _, _) = spawn_server(["foo"]).await?;

    let project = server.project("foo", Vec::<String>::new())?;

    project
        .wit(["add", "foo:bar"])
        .assert()
        .stderr(contains("package `foo:bar` was not found"))
        .failure();

    Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn validate_the_version_exists() -> Result<()> {
    let (server, _, _) = spawn_server(Vec::<String>::new()).await?;

    let project = server.project("foo", Vec::<String>::new())?;
    project.file("foo.wit", "package test:bar;\n")?;
    project
        .wit(["publish"])
        .env("WIT_PUBLISH_KEY", test_signing_key())
        .assert()
        .stderr(contains("Published package `test:bar` v0.1.0"))
        .success();

    let project = server.project("bar", Vec::<String>::new())?;
    project
        .wit(["add", "test:bar"])
        .assert()
        .stderr(contains("Added dependency `test:bar` with version `0.1.0`"))
        .success();

    let manifest = fs::read_to_string(project.root().join("wit.toml"))?;
    assert!(contains(r#""test:bar" = "0.1.0""#).eval(&manifest));

    project
        .wit(["add", "--name", "test:bar2", "test:bar@2.0.0"])
        .assert()
        .stderr(contains(
            "component registry package `test:bar` has no release matching version requirement `^2.0.0`",
        ))
        .failure();

    Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn checks_for_duplicate_dependencies() -> Result<()> {
    let (server, _, _) = spawn_server(Vec::<String>::new()).await?;

    let project = server.project("foo", Vec::<String>::new())?;
    project.file("foo.wit", "package test:bar;\n")?;
    project
        .wit(["publish"])
        .env("WIT_PUBLISH_KEY", test_signing_key())
        .assert()
        .stderr(contains("Published package `test:bar` v0.1.0"))
        .success();

    let project = server.project("bar", Vec::<String>::new())?;
    project
        .wit(["add", "test:bar"])
        .assert()
        .stderr(contains("Added dependency `test:bar` with version `0.1.0`"))
        .success();

    let manifest = fs::read_to_string(project.root().join("wit.toml"))?;
    assert!(contains(r#""test:bar" = "0.1.0""#).eval(&manifest));

    project
        .wit(["add", "test:bar"])
        .assert()
        .stderr(contains(
            "cannot add dependency `test:bar` as it conflicts with an existing dependency",
        ))
        .failure();

    Ok(())
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn does_not_modify_manifest_for_dry_run() -> Result<()> {
    let (server, _, _) = spawn_server(Vec::<String>::new()).await?;

    let project = server.project("foo", Vec::<String>::new())?;
    project.file("foo.wit", "package test:bar;\n")?;
    project
        .wit(["publish"])
        .env("WIT_PUBLISH_KEY", test_signing_key())
        .assert()
        .stderr(contains("Published package `test:bar` v0.1.0"))
        .success();

    let project = server.project("bar", Vec::<String>::new())?;
    project
        .wit(["add", "test:bar", "--dry-run"])
        .assert()
        .stderr(contains(
            "Would add dependency `test:bar` with version `0.1.0` (dry run)",
        ))
        .success();

    let manifest = fs::read_to_string(project.root().join("wit.toml"))?;
    assert!(!contains("test:bar").eval(&manifest));

    Ok(())
}

#[test]
fn validate_add_from_path() -> Result<()> {
    let project = Project::new("foo")?;

    project
        .wit(["add", "--path", "foo/baz", "foo:baz"])
        .assert()
        .stderr(contains("Added dependency `foo:baz` from path `foo/baz`"));

    let manifest = fs::read_to_string(project.root().join("wit.toml"))?;
    assert!(contains(r#""foo:baz" = { path = "foo/baz" }"#).eval(&manifest));

    Ok(())
}