Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/skade

serve/tests/serve_roundtrip.rs

//! Functional test for `skade-katalog serve`: one lock-owning catalog fronted
//! over BOTH wires at once. Binds REST + gRPC on ephemeral ports over a single
//! `RedbCatalog`, creates a table over the **gRPC** front, then loads it over
//! the **REST** front — proving both fronts share the one backing catalog (the
//! serve-level guarantee), and that the binds actually happen.

use std::collections::HashMap;
use std::sync::Arc;

use iceberg::io::LocalFsStorageFactory;
use iceberg::spec::{NestedField, PrimitiveType, Schema, Type};
use iceberg::{Catalog, CatalogBuilder, NamespaceIdent, TableIdent};
use iceberg_catalog_rest::{
    REST_CATALOG_PROP_URI, REST_CATALOG_PROP_WAREHOUSE, RestCatalogBuilder,
};
use skade_katalog_grpc::pb;
use skade_katalog_serve::ServeConfig;
use tempfile::TempDir;

fn schema() -> Schema {
    Schema::builder()
        .with_schema_id(0)
        .with_fields(vec![
            NestedField::required(1, "id", Type::Primitive(PrimitiveType::Long)).into(),
        ])
        .build()
        .expect("schema")
}

#[tokio::test]
async fn serve_fronts_one_catalog_over_rest_and_grpc() {
    let tmp = TempDir::new().unwrap();
    let mut cfg = ServeConfig::new(tmp.path().join("wh"));
    cfg.rest = Some("127.0.0.1:0".parse().unwrap());
    cfg.grpc = Some("127.0.0.1:0".parse().unwrap());

    let (rest_addr, grpc_addr) = skade_katalog_serve::spawn(&cfg).await.expect("serve spawn");
    let rest_addr = rest_addr.expect("rest bound");
    let grpc_addr = grpc_addr.expect("grpc bound");

    // --- create a table over the gRPC front ---
    let mut grpc = pb::catalog_client::CatalogClient::connect(format!("http://{grpc_addr}"))
        .await
        .expect("grpc connect");
    grpc.create_namespace(pb::CreateNamespaceReq {
        levels: vec!["db".into()],
        properties: HashMap::new(),
    })
    .await
    .expect("grpc create_namespace");
    let created = grpc
        .create_table(pb::CreateTableReq {
            namespace: vec!["db".into()],
            name: "shared".into(),
            schema_json: serde_json::to_string(&schema()).unwrap(),
            location: String::new(),
        })
        .await
        .expect("grpc create_table")
        .into_inner();

    // --- load the SAME table over the REST front ---
    let rest = RestCatalogBuilder::default()
        .with_storage_factory(Arc::new(LocalFsStorageFactory))
        .load(
            "rest",
            HashMap::from([
                (
                    REST_CATALOG_PROP_URI.to_string(),
                    format!("http://{rest_addr}"),
                ),
                (
                    REST_CATALOG_PROP_WAREHOUSE.to_string(),
                    "warehouse".to_string(),
                ),
            ]),
        )
        .await
        .expect("rest client");

    let ident = TableIdent::new(NamespaceIdent::new("db".to_string()), "shared".to_string());
    let loaded = rest
        .load_table(&ident)
        .await
        .expect("REST load of a gRPC-created table");
    assert_eq!(
        loaded.metadata_location().unwrap(),
        created.metadata_location,
        "REST and gRPC fronts see the one shared catalog's pointer"
    );
}