forklaunch 1.35.0

Launch faster with forklaunch
use super::*;
use std::path::Path;

const BILLING: &str = "import {\n  number,\n  optional,\n  schemaValidator,\n  SchemaValidator,\n  string\n} from '@demo/core';\nimport { OpenTelemetryCollector } from '@forklaunch/core/http';\nimport { createConfigInjector, getEnvVar, Lifetime } from '@forklaunch/core/services';\nimport Stripe from 'stripe';\n\nconst configInjector = createConfigInjector(SchemaValidator(), {\n  STRIPE_API_KEY: {\n    lifetime: Lifetime.Singleton,\n    type: string,\n    value: getEnvVar('STRIPE_API_KEY')\n  },\n  STRIPE_WEBHOOK_SECRET: {\n    lifetime: Lifetime.Singleton,\n    type: string,\n    value: getEnvVar('STRIPE_WEBHOOK_SECRET')\n  }\n});\nexport const environmentConfig = configInjector.chain({});\nconst runtimeDependencies = environmentConfig.chain({\n  StripeClient: {\n    lifetime: Lifetime.Singleton,\n    type: Stripe,\n    factory: ({ STRIPE_API_KEY }) => new Stripe(STRIPE_API_KEY)\n  }\n});\n";

fn edit_for(dir: &Path) -> CapabilityEdit {
    CapabilityEdit::new(
        dir,
        "demo",
        "billing",
        &dir.join("billing"),
        vec!["payments".to_string()],
        vec!["payments".to_string()],
    )
}

#[test]
fn named_imports_are_merged_and_dropped_without_losing_others() {
    let text = "import { OpenTelemetryCollector } from '@forklaunch/core/http';\nconst a = 1;\n";
    let added = ensure_named_import(text, "createStripeClient", CORE_HTTP);
    assert!(added.contains(
        "import { OpenTelemetryCollector, createStripeClient } from '@forklaunch/core/http';"
    ));
    assert_eq!(
        ensure_named_import(&added, "createStripeClient", CORE_HTTP),
        added
    );
    let removed = in_place::remove_named_imports(&added, CORE_HTTP, &["createStripeClient"]);
    assert_eq!(removed, text);
    let fresh = ensure_named_import(
        "import x from 'y';\nconst a = 1;\n",
        "createStripeClient",
        CORE_HTTP,
    );
    assert_eq!(
        fresh,
        "import { createStripeClient } from '@forklaunch/core/http';\nimport x from 'y';\nconst a = 1;\n"
    );
    assert_eq!(
        in_place::remove_named_imports(&fresh, CORE_HTTP, &["createStripeClient"]),
        "import x from 'y';\nconst a = 1;\n"
    );
}

#[test]
fn the_default_stripe_import_is_added_once_in_the_files_quotes() {
    let text = "import {\n  createConfigInjector,\n  getEnvVar\n} from \"@forklaunch/core/services\";\nimport { a } from \"./a\";\n";
    let added = in_place::add_default_import(text, "Stripe", "stripe");
    assert_eq!(
        added,
        "import {\n  createConfigInjector,\n  getEnvVar\n} from \"@forklaunch/core/services\";\nimport Stripe from \"stripe\";\nimport { a } from \"./a\";\n"
    );
    assert_eq!(
        in_place::add_default_import(&added, "Stripe", "stripe"),
        added
    );
}

#[test]
fn keys_are_made_optional_and_back() {
    let optional = retype_keys(BILLING, "string", "optional(string)");
    assert_eq!(optional.matches("type: optional(string)").count(), 2);
    assert_eq!(
        retype_keys(&optional, "optional(string)", "string"),
        BILLING
    );
}

#[test]
fn an_existing_stripe_client_is_switched_not_duplicated_and_restored() {
    let dir = std::env::temp_dir().join("fl-payments-cap-billing");
    let _ = std::fs::remove_dir_all(&dir);
    std::fs::create_dir_all(dir.join("billing/api/controllers")).unwrap();
    std::fs::write(dir.join("billing/registrations.ts"), BILLING).unwrap();
    std::fs::write(
        dir.join("billing/server.ts"),
        "import x from 'y';\nconst app = f();\napp.use(a);\n",
    )
    .unwrap();
    std::fs::write(
        dir.join("billing/api/controllers/webhook.controller.ts"),
        "const STRIPE_WEBHOOK_SECRET = ci.resolve(tokens.STRIPE_WEBHOOK_SECRET);\n",
    )
    .unwrap();

    let mut edit = edit_for(&dir);
    add(&mut edit).unwrap();
    edit.commit().unwrap();
    let text = std::fs::read_to_string(dir.join("billing/registrations.ts")).unwrap();
    assert_eq!(text.matches("StripeClient:").count(), 1);
    assert!(text.contains(SWITCHED_FACTORY));
    assert!(text.contains(
        "import { OpenTelemetryCollector, createStripeClient } from '@forklaunch/core/http';"
    ));
    assert_eq!(text.matches("type: optional(string)").count(), 2);
    let controller =
        std::fs::read_to_string(dir.join("billing/api/controllers/webhook.controller.ts")).unwrap();
    assert!(controller.contains("?? ''"));
    let handler =
        std::fs::read_to_string(dir.join("billing/api/platformEvents/payments.ts")).unwrap();
    assert!(handler.contains("export async function handle(event: PlatformEvent)"));
    assert!(handler.contains("processed.has(event.id)"));

    let mut edit = edit_for(&dir);
    edit.service_capabilities.clear();
    remove(&mut edit).unwrap();
    edit.commit().unwrap();
    assert_eq!(
        std::fs::read_to_string(dir.join("billing/registrations.ts")).unwrap(),
        BILLING
    );
    assert!(
        std::fs::read_to_string(dir.join("billing/api/controllers/webhook.controller.ts"))
            .unwrap()
            .ends_with("tokens.STRIPE_WEBHOOK_SECRET);\n")
    );
    let _ = std::fs::remove_dir_all(&dir);
}

#[test]
fn the_blueprints_managed_ready_registration_is_left_alone() {
    let dir = std::env::temp_dir().join("fl-payments-cap-native");
    let _ = std::fs::remove_dir_all(&dir);
    std::fs::create_dir_all(dir.join("billing")).unwrap();
    let native = BILLING.replace(
            "factory: ({ STRIPE_API_KEY }) => new Stripe(STRIPE_API_KEY)",
            "factory: ({ STRIPE_API_KEY }) => { if (isManagedInstance()) return createStripeClient({ Stripe }); return new Stripe(STRIPE_API_KEY!); }",
        );
    std::fs::write(dir.join("billing/registrations.ts"), &native).unwrap();
    std::fs::write(
        dir.join("billing/server.ts"),
        "const app = f();\napp.use(a);\n",
    )
    .unwrap();
    let mut edit = edit_for(&dir);
    add(&mut edit).unwrap();
    edit.commit().unwrap();
    assert_eq!(
        std::fs::read_to_string(dir.join("billing/registrations.ts")).unwrap(),
        native
    );
    let mut edit = edit_for(&dir);
    remove(&mut edit).unwrap();
    edit.commit().unwrap();
    assert_eq!(
        std::fs::read_to_string(dir.join("billing/registrations.ts")).unwrap(),
        native
    );
    let _ = std::fs::remove_dir_all(&dir);
}

#[test]
fn an_unknown_stripe_factory_is_refused_rather_than_guessed() {
    let dir = std::env::temp_dir().join("fl-payments-cap-unknown");
    let _ = std::fs::remove_dir_all(&dir);
    std::fs::create_dir_all(dir.join("billing")).unwrap();
    std::fs::write(
        dir.join("billing/registrations.ts"),
        BILLING.replace(
            "new Stripe(STRIPE_API_KEY)",
            "new Stripe(STRIPE_API_KEY, { apiVersion })",
        ),
    )
    .unwrap();
    let mut edit = edit_for(&dir);
    let err = add(&mut edit).unwrap_err().to_string();
    assert!(err.contains("createStripeClient"), "{err}");
    let _ = std::fs::remove_dir_all(&dir);
}

#[test]
fn the_capability_is_registered() {
    let c = super::super::find("payments").expect("payments is in CAPABILITIES");
    assert_eq!(c.resource_type, "payment");
    assert_eq!(c.registration_key, "StripeClient");
    assert!(c.receives_events);
}