wit 0.16.0

A tool for building and publishing WIT packages to a registry.
Documentation
use anyhow::{Context, Result};
use assert_cmd::prelude::*;
use futures::TryStreamExt;
use predicates::str::contains;
use toml_edit::{value, Array};
use wasm_metadata::LinkType;
use wasm_pkg_client::{Client, Error};

use crate::support::*;

mod support;

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

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

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

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

    Ok(())
}

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

    let project = server.project("foo", Vec::<String>::new())?;
    project.file("baz.wit", "package test:qux;\n")?;
    project
        .wit(["publish", "--dry-run"])
        .env("WIT_PUBLISH_KEY", test_signing_key())
        .assert()
        .stderr(contains(
            "warning: not publishing package to the registry due to the --dry-run option",
        ))
        .success();

    let client = Client::new(config);

    let err = client
        .get_release(&"test:qux".parse().unwrap(), &"0.1.0".parse().unwrap())
        .await
        .expect_err("Should not be able to get release after dry run");
    assert!(
        matches!(err, Error::PackageNotFound),
        "Expected PackageNotFound"
    );

    Ok(())
}

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

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

    let authors = ["Jane Doe <jane@example.com>"];
    let categories = ["wasm"];
    let description = "A test package";
    let license = "Apache-2.0";
    let documentation = "https://example.com/docs";
    let homepage = "https://example.com/home";
    let repository = "https://example.com/repo";

    project.file("baz.wit", "package test:qux;\n")?;

    project.update_manifest(|mut doc| {
        doc["authors"] = value(Array::from_iter(authors));
        doc["categories"] = value(Array::from_iter(categories));
        doc["description"] = value(description);
        doc["license"] = value(license);
        doc["documentation"] = value(documentation);
        doc["homepage"] = value(homepage);
        doc["repository"] = value(repository);
        Ok(doc)
    })?;

    project
        .wit(["publish"])
        .env("WIT_PUBLISH_KEY", test_signing_key())
        .assert()
        .stderr(contains("Published package `test:qux` v0.1.0"))
        .success();

    let client = Client::new(config);
    let package_ref = "test:qux".parse().unwrap();
    let release = client
        .get_release(&package_ref, &"0.1.0".parse().unwrap())
        .await?;
    let stream = client.stream_content(&package_ref, &release).await?;

    let bytes = stream.map_ok(Vec::from).try_concat().await?;

    let metadata = wasm_metadata::RegistryMetadata::from_wasm(&bytes)
        .context("failed to parse registry metadata from bytes")?
        .expect("missing registry metadata");

    assert_eq!(
        metadata.get_authors().expect("missing authors").as_slice(),
        authors
    );
    assert_eq!(
        metadata
            .get_categories()
            .expect("missing categories")
            .as_slice(),
        categories
    );
    assert_eq!(
        metadata.get_description().expect("missing description"),
        description
    );
    assert_eq!(metadata.get_license().expect("missing license"), license);

    let links = metadata.get_links().expect("missing links");
    assert_eq!(links.len(), 3);

    assert_eq!(
        links
            .iter()
            .find(|link| link.ty == LinkType::Documentation)
            .expect("missing documentation")
            .value,
        documentation
    );
    assert_eq!(
        links
            .iter()
            .find(|link| link.ty == LinkType::Homepage)
            .expect("missing homepage")
            .value,
        homepage
    );
    assert_eq!(
        links
            .iter()
            .find(|link| link.ty == LinkType::Repository)
            .expect("missing repository")
            .value,
        repository
    );

    Ok(())
}