Skip to content

Commit

Permalink
feat: do not fail on non-existent namespace in Rust core (#26)
Browse files Browse the repository at this point in the history
* feat: do not fail on non-existent namespace in Rust core

* chore: fix ruby tests descriptions

* chore: fix node test description
  • Loading branch information
yquansah authored Dec 7, 2023
1 parent 9bd22ea commit 51ef54b
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 16 deletions.
28 changes: 28 additions & 0 deletions flipt-client-go/evaluation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,31 @@ func TestBoolean(t *testing.T) {
assert.True(t, boolean.Result.Enabled)
assert.Equal(t, "MATCH_EVALUATION_REASON", boolean.Result.Reason)
}

func TestVariantFailure(t *testing.T) {
evaluationClient, err := evaluation.NewClient(evaluation.WithURL(fliptUrl), evaluation.WithAuthToken(authToken))
require.NoError(t, err)

variant, err := evaluationClient.EvaluateVariant(context.TODO(), "nonexistent", "someentity", map[string]string{
"fizz": "buzz",
})
require.NoError(t, err)

assert.Nil(t, variant.Result)
assert.Equal(t, "failure", variant.Status)
assert.Equal(t, "failed to get flag information default/nonexistent", variant.ErrorMessage)
}

func TestBooleanFailure(t *testing.T) {
evaluationClient, err := evaluation.NewClient(evaluation.WithURL(fliptUrl), evaluation.WithAuthToken(authToken))
require.NoError(t, err)

boolean, err := evaluationClient.EvaluateBoolean(context.TODO(), "nonexistent", "someentity", map[string]string{
"fizz": "buzz",
})
require.NoError(t, err)

assert.Nil(t, boolean.Result)
assert.Equal(t, "failure", boolean.Status)
assert.Equal(t, "failed to get flag information default/nonexistent", boolean.ErrorMessage)
}
34 changes: 34 additions & 0 deletions flipt-client-node/src/evaluation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,37 @@ test('boolean', () => {
expect(boolean.result.enabled).toEqual(true);
expect(boolean.result.reason).toEqual('MATCH_EVALUATION_REASON');
});

test('variant failure', () => {
const fec = new FliptEvaluationClient('default', {
url: fliptUrl,
auth_token: authToken
});

const variant = fec.evaluateVariant('nonexistent', 'someentity', {
fizz: 'buzz'
});

expect(variant.result).toBeNull();
expect(variant.status).toEqual('failure');
expect(variant.error_message).toEqual(
'failed to get flag information default/nonexistent'
);
});

test('boolean failure', () => {
const fec = new FliptEvaluationClient('default', {
url: fliptUrl,
auth_token: authToken
});

const boolean = fec.evaluateVariant('nonexistent', 'someentity', {
fizz: 'buzz'
});

expect(boolean.result).toBeNull();
expect(boolean.status).toEqual('failure');
expect(boolean.error_message).toEqual(
'failed to get flag information default/nonexistent'
);
});
20 changes: 20 additions & 0 deletions flipt-client-python/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@ def test_boolean(self):
self.assertEqual("flag_boolean", boolean.result.flag_key)
self.assertEqual("MATCH_EVALUATION_REASON", boolean.result.reason)

def test_failure_variant(self):
variant = self.flipt_client.evaluate_variant(
"nonexistent", "someentity", {"fizz": "buzz"}
)
self.assertIsNone(variant.result)
self.assertEqual(
variant.error_message, "failed to get flag information default/nonexistent"
)
self.assertEqual("failure", variant.status)

def test_failure_boolean(self):
boolean = self.flipt_client.evaluate_boolean(
"nonexistent", "someentity", {"fizz": "buzz"}
)
self.assertIsNone(boolean.result)
self.assertEqual(
boolean.error_message, "failed to get flag information default/nonexistent"
)
self.assertEqual("failure", boolean.status)


if __name__ == "__main__":
unittest.main()
30 changes: 30 additions & 0 deletions flipt-client-ruby/spec/evaluation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,34 @@
expect(resp['result']['reason']).to eq('MATCH_EVALUATION_REASON')
end
end

describe '#evaluate_variant failure' do
it 'gracefully handles failures for variant flag evaluation' do
url = ENV.fetch('FLIPT_URL', 'http://localhost:8080')
auth_token = ENV.fetch('FLIPT_AUTH_TOKEN', 'secret')
client = Flipt::EvaluationClient.new('default', { url: url, auth_token: auth_token })

resp = client.evaluate_variant({ flag_key: 'nonexistent', entity_id: 'someentity', context: { "fizz": 'buzz' } })

expect(resp).to_not be_nil
expect(resp['result']).to be_nil
expect(resp['status']).to eq('failure')
expect(resp['error_message']).to eq('failed to get flag information default/nonexistent')
end
end

describe '#evaluate_boolean failure' do
it 'gracefully handles failures for boolean flag evaluation' do
url = ENV.fetch('FLIPT_URL', 'http://localhost:8080')
auth_token = ENV.fetch('FLIPT_AUTH_TOKEN', 'secret')
client = Flipt::EvaluationClient.new('default', { url: url, auth_token: auth_token })

resp = client.evaluate_boolean({ flag_key: 'nonexistent', entity_id: 'someentity', context: { "fizz": 'buzz' } })

expect(resp).to_not be_nil
expect(resp['result']).to be_nil
expect(resp['status']).to eq('failure')
expect(resp['error_message']).to eq('failed to get flag information default/nonexistent')
end
end
end
2 changes: 1 addition & 1 deletion flipt-engine/examples/evaluation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fn main() {
parser::HTTPParser::new("http://localhost:8080", Some("secret")),
);

let eng = fliptengine::Engine::new(evaluator.unwrap(), Default::default());
let eng = fliptengine::Engine::new(evaluator, Default::default());
let mut context: HashMap<String, String> = HashMap::new();
context.insert("fizz".into(), "buzz".into());

Expand Down
12 changes: 6 additions & 6 deletions flipt-engine/src/evaluator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,14 @@ impl<P> Evaluator<P, Snapshot>
where
P: Parser + Send,
{
pub fn new_snapshot_evaluator(namespaces: Vec<String>, parser: P) -> Result<Self, Whatever> {
let snap = Snapshot::build(&namespaces, &parser)?;
pub fn new_snapshot_evaluator(namespaces: Vec<String>, parser: P) -> Self {
let snap = Snapshot::build(&namespaces, &parser);
Evaluator::new(namespaces, parser, snap)
}

pub fn replace_snapshot(&mut self) {
let snap = Snapshot::build(&self.namespaces, &self.parser);
self.replace_store(snap.unwrap());
self.replace_store(snap);
}
}

Expand All @@ -125,13 +125,13 @@ where
P: Parser + Send,
S: Store + Send,
{
pub fn new(namespaces: Vec<String>, parser: P, store_impl: S) -> Result<Self, Whatever> {
Ok(Self {
pub fn new(namespaces: Vec<String>, parser: P, store_impl: S) -> Self {
Self {
namespaces,
parser,
store: store_impl,
mtx: Arc::new(RwLock::new(0)),
})
}
}

pub fn replace_store(&mut self, store_impl: S) {
Expand Down
2 changes: 1 addition & 1 deletion flipt-engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ pub unsafe extern "C" fn initialize_engine(
let parser = parser::HTTPParser::new(&http_url, auth_token.clone().as_deref());
let evaluator = evaluator::Evaluator::new_snapshot_evaluator(namespaces_vec, parser);

Box::into_raw(Box::new(Engine::new(evaluator.unwrap(), engine_opts))) as *mut c_void
Box::into_raw(Box::new(Engine::new(evaluator, engine_opts))) as *mut c_void
}

/// # Safety
Expand Down
12 changes: 12 additions & 0 deletions flipt-engine/src/models/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ pub struct Document {
pub flags: Vec<Flag>,
}

impl Default for Document {
fn default() -> Self {
Self {
namespace: Namespace {
key: "".into(),
name: None,
},
flags: Vec::new(),
}
}
}

#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Namespace {
Expand Down
13 changes: 5 additions & 8 deletions flipt-engine/src/store/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::models::common;
use crate::models::flipt;
use crate::models::source::Document;
use crate::parser::Parser;

#[cfg(test)]
use mockall::automock;
use snafu::Whatever;
use std::collections::HashMap;

#[cfg_attr(test, automock)]
Expand Down Expand Up @@ -40,14 +40,14 @@ struct Namespace {
}

impl Snapshot {
pub fn build<P>(namespaces: &Vec<String>, parser: &P) -> Result<Snapshot, Whatever>
pub fn build<P>(namespaces: &Vec<String>, parser: &P) -> Snapshot
where
P: Parser + Send,
{
let mut ns: HashMap<String, Namespace> = HashMap::new();

for n in namespaces {
let doc = parser.parse(n)?;
let doc = parser.parse(n).unwrap_or(Document::default());

let mut flags: HashMap<String, flipt::Flag> = HashMap::new();
let mut eval_rules: HashMap<String, Vec<flipt::EvaluationRule>> = HashMap::new();
Expand Down Expand Up @@ -207,7 +207,7 @@ impl Snapshot {
);
}

Ok(Self { namespaces: ns })
Self { namespaces: ns }
}
}

Expand Down Expand Up @@ -268,10 +268,7 @@ mod tests {
fn test_snapshot() {
let tp = TestParser::new();

let snapshot = match Snapshot::build(&vec!["default".into()], &tp) {
Ok(s) => s,
Err(e) => panic!("{}", e),
};
let snapshot = Snapshot::build(&vec!["default".into()], &tp);

let flag_variant = snapshot
.get_flag("default", "flag1")
Expand Down

0 comments on commit 51ef54b

Please sign in to comment.