Skip to content

Commit

Permalink
Merge pull request #19 from fermyon/redis
Browse files Browse the repository at this point in the history
Redis Tests
  • Loading branch information
rylev committed Jun 26, 2024
2 parents 6e30479 + a49bdd4 commit 5aa8463
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions components/outbound-redis/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "outbound-redis-test-component"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
anyhow = { workspace = true }
wit-bindgen = { workspace = true }
107 changes: 107 additions & 0 deletions components/outbound-redis/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use anyhow::Context as _;
use bindings::{
exports::wasi::http0_2_0::incoming_handler::Guest,
fermyon::spin2_0_0::redis,
wasi::http0_2_0::types::{
Headers, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam,
},
};

struct Component;

mod bindings {
wit_bindgen::generate!({
world: "http-trigger",
path: "../../wit",
});
use super::Component;
export!(Component);
}

impl Guest for Component {
fn handle(request: IncomingRequest, response_out: ResponseOutparam) {
let result = match handle(request) {
Err(e) => response(500, format!("{e}").as_bytes()),
Ok(r) => r,
};
ResponseOutparam::set(response_out, Ok(result))
}
}

const REDIS_ADDRESS_HEADER: &str = "REDIS_ADDRESS";

fn handle(request: IncomingRequest) -> anyhow::Result<OutgoingResponse> {
let Some(address) = request
.headers()
.get(&REDIS_ADDRESS_HEADER.to_owned())
.pop()
.and_then(|v| String::from_utf8(v).ok())
else {
// Otherwise, return a 400 Bad Request response.
return Ok(response(400, b"Bad Request"));
};
let connection = redis::Connection::open(&address)?;

connection.set("spin-example-get-set", &b"Eureka!".to_vec())?;

let payload = connection
.get("spin-example-get-set")?
.context("missing value for 'spin-example-get-set'")?;

anyhow::ensure!(String::from_utf8_lossy(&payload) == "Eureka!");

connection.set("spin-example-incr", &b"0".to_vec())?;

let int_value = connection.incr("spin-example-incr")?;

anyhow::ensure!(int_value == 1);

let keys = vec!["spin-example-get-set".into(), "spin-example-incr".into()];

let del_keys = connection.del(&keys)?;

anyhow::ensure!(del_keys == 2);

connection.execute(
"set",
&[
redis::RedisParameter::Binary(b"spin-example".to_vec()),
redis::RedisParameter::Binary(b"Eureka!".to_vec()),
],
)?;

connection.execute(
"append",
&[
redis::RedisParameter::Binary(b"spin-example".to_vec()),
redis::RedisParameter::Binary(b" I've got it!".to_vec()),
],
)?;

let values = connection.execute(
"get",
&[redis::RedisParameter::Binary(b"spin-example".to_vec())],
)?;

anyhow::ensure!(matches!(
values.as_slice(),
&[redis::RedisResult::Binary(ref b)] if b == b"Eureka! I've got it!"));

Ok(response(200, b""))
}

fn response(status: u16, body: &[u8]) -> OutgoingResponse {
let response = OutgoingResponse::new(Headers::new());
response.set_status_code(status).unwrap();
if !body.is_empty() {
assert!(body.len() <= 4096);
let outgoing_body = response.body().unwrap();
{
let outgoing_stream = outgoing_body.write().unwrap();
outgoing_stream.blocking_write_and_flush(body).unwrap();
// The outgoing stream must be dropped before the outgoing body is finished.
}
OutgoingBody::finish(outgoing_body, None).unwrap();
}
response
}
2 changes: 2 additions & 0 deletions crates/conformance-tests/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ pub enum Precondition {
TcpEcho,
/// The test expects a sqlite service to be available.
Sqlite,
/// The test expects a Redis service to be available.
Redis,
}

#[derive(Debug, Clone, serde::Deserialize)]
Expand Down
14 changes: 14 additions & 0 deletions tests/outbound-redis-no-permission/spin.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
spin_manifest_version = 2

[application]
name = "outbound-redis"
authors = ["Fermyon Engineering <engineering@fermyon.com>"]
version = "0.1.0"

[[trigger.http]]
route = "/"
component = "test"

[component.test]
source = "%{source=outbound-redis}"
environment = { REDIS_ADDRESS = "redis://localhost:6379" }
38 changes: 38 additions & 0 deletions tests/outbound-redis-no-permission/test.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"invocations": [
{
"request": {
"path": "/",
"headers": [
{
"name": "Host",
"value": "example.com"
},
{
"name": "redis_address",
"value": "redis://localhost:%{port=6379}",
}
]
},
"response": {
"status": 500,
"headers": [
{
"name": "Content-Length",
"optional": true,
},
{
"name": "transfer-encoding",
"optional": true
},
{
"name": "Date",
"optional": true
}
],
"body": "Error::InvalidAddress"
}
}
],
"preconditions": [ { "kind": "redis" } ]
}
18 changes: 18 additions & 0 deletions tests/outbound-redis-variable-permission/spin.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
spin_manifest_version = 2

[application]
name = "outbound-redis"
authors = ["Fermyon Engineering <engineering@fermyon.com>"]
version = "0.1.0"

[variables]
redis_host = { default = "localhost" }

[[trigger.http]]
route = "/"
component = "test"

[component.test]
source = "%{source=outbound-redis}"
environment = { REDIS_ADDRESS = "redis://localhost:%{port=6379}" }
allowed_outbound_hosts = ["redis://{{ redis_host }}:%{port=6379}"]
32 changes: 32 additions & 0 deletions tests/outbound-redis-variable-permission/test.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"invocations": [
{
"request": {
"path": "/",
"headers": [
{
"name": "Host",
"value": "example.com"
},
{
"name": "redis_address",
"value": "redis://localhost:%{port=6379}",
}
]
},
"response": {
"headers": [
{
"name": "Content-Length",
"value": "0"
},
{
"name": "Date",
"optional": true
}
],
}
}
],
"preconditions": [ { "kind": "redis" } ]
}
14 changes: 14 additions & 0 deletions tests/outbound-redis/spin.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
spin_manifest_version = 2

[application]
name = "outbound-redis"
authors = ["Fermyon Engineering <engineering@fermyon.com>"]
version = "0.1.0"

[[trigger.http]]
route = "/"
component = "test"

[component.test]
source = "%{source=outbound-redis}"
allowed_outbound_hosts = ["redis://localhost:%{port=6379}"]
32 changes: 32 additions & 0 deletions tests/outbound-redis/test.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"invocations": [
{
"request": {
"path": "/",
"headers": [
{
"name": "Host",
"value": "example.com"
},
{
"name": "redis_address",
"value": "redis://localhost:%{port=6379}",
}
]
},
"response": {
"headers": [
{
"name": "Content-Length",
"value": "0"
},
{
"name": "Date",
"optional": true
}
]
}
}
],
"preconditions": [ { "kind": "redis" } ]
}

0 comments on commit 5aa8463

Please sign in to comment.