dev_codec_cookie_test_vectors.erl - Cookie Codec Test Vectors
Overview
Purpose: Comprehensive test suite for cookie parsing and encoding correctness
Module: dev_codec_cookie_test_vectors
Test Type: Property-based equivalence testing
Coverage: Cookie parsing, serialization, attributes, flags
This module provides a battery of test vectors for validating the dev_codec_cookie codec implementation. It uses equivalence classes to test that various input formats all produce the same normalized output, ensuring robust parsing and encoding.
Dependencies
- Testing:
eunit - Codecs:
dev_codec_cookie - HyperBEAM:
hb_maps - Includes:
include/hb.hrl
Internal Functions Overview
This module contains only internal (non-exported) functions used for testing:
%% Test Utilities (internal)
test_data() -> TestDataMap.
assert_set(TestSetName, Function) -> ok.
to_string(CookieMsg) -> SetCookieStrings.
from_string(String) -> CookieMsg.
%% EUnit Tests (internal)
from_string_basic_test() -> ok.
from_string_attributes_test() -> ok.
from_string_flags_test() -> ok.
to_string_basic_test() -> ok.
to_string_attributes_test() -> ok.
to_string_flags_test() -> ok.
%% ... and many more test functionsInternal Functions
1. test_data/0
test_data() -> TestDataMap
when
TestDataMap :: #{
TestSetName => {[Input], ExpectedOutput}
}.Description: Returns a map of test sets where each entry contains a list of equivalent inputs and their expected normalized output. All inputs should produce the same output when processed.
Test Sets Included:from_string_raw_value- Simple key-value cookie parsingfrom_string_attributes- Cookie attributes parsingfrom_string_flags- Cookie flags parsingto_string_raw_value- Cookie serialization with raw valuesto_string_attributes- Cookie serialization with attributesto_string_flags- Cookie serialization with flags- Real-world test cases for various cookie scenarios
-module(test_dev_codec_cookie_test_vectors).
-include_lib("eunit/include/eunit.hrl").
cookie_parse_simple_test() ->
% Test basic cookie parsing via dev_codec_cookie
{ok, Msg} = dev_codec_cookie:from(
#{ <<"set-cookie">> => <<"k1=v1">> },
#{},
#{}
),
{ok, Cookie} = dev_codec_cookie:extract(Msg, #{}, #{}),
?assertEqual(<<"v1">>, maps:get(<<"k1">>, Cookie)).
cookie_parse_quoted_test() ->
% Test quoted cookie value parsing
{ok, Msg} = dev_codec_cookie:from(
#{ <<"set-cookie">> => <<"k1=\"v1\"">> },
#{},
#{}
),
{ok, Cookie} = dev_codec_cookie:extract(Msg, #{}, #{}),
?assertEqual(<<"v1">>, maps:get(<<"k1">>, Cookie)).2. assert_set/2
assert_set(TestSet, Fun) -> ok
when
TestSet :: atom(),
Fun :: fun((Input) -> Output).Description: Assert that when given the inputs in the test set, the outputs are all equal to the expected value when the function is applied to them. This implements equivalence class testing.
Test Code:cookie_parse_attributes_test() ->
% Test cookie with attributes parsing
{ok, Msg} = dev_codec_cookie:from(
#{ <<"set-cookie">> => <<"k1=v1; Path=/">> },
#{},
#{}
),
{ok, Cookie} = dev_codec_cookie:extract(Msg, #{}, #{}),
K1 = maps:get(<<"k1">>, Cookie),
?assert(is_map(K1)),
?assertEqual(<<"v1">>, maps:get(<<"value">>, K1)).
cookie_parse_flags_test() ->
% Test cookie with flags parsing
{ok, Msg} = dev_codec_cookie:from(
#{ <<"set-cookie">> => <<"k1=v1; Secure; HttpOnly">> },
#{},
#{}
),
{ok, Cookie} = dev_codec_cookie:extract(Msg, #{}, #{}),
K1 = maps:get(<<"k1">>, Cookie),
?assert(maps:is_key(<<"flags">>, K1)).3. to_string/1
to_string(CookieMsg) -> SetCookieStrings
when
CookieMsg :: map(),
SetCookieStrings :: [binary()].Description: Convert a cookie message to a list of Set-Cookie header strings. Uses dev_codec_cookie:store/3 and dev_codec_cookie:to/3 internally.
cookie_to_string_test() ->
Opts = hb_private:opts(#{}),
Msg = hb_private:set(#{}, <<"cookie">>, #{
<<"k1">> => <<"v1">>
}, Opts),
{ok, Result} = dev_codec_cookie:to(Msg, #{ <<"format">> => <<"set-cookie">> }, #{}),
SetCookies = maps:get(<<"set-cookie">>, Result),
?assert(is_list(SetCookies)),
?assertEqual(1, length(SetCookies)).
cookie_to_cookie_format_test() ->
Opts = hb_private:opts(#{}),
Msg = hb_private:set(#{}, <<"cookie">>, #{
<<"k1">> => <<"v1">>,
<<"k2">> => <<"v2">>
}, Opts),
{ok, Result} = dev_codec_cookie:to(Msg, #{ <<"format">> => <<"cookie">> }, #{}),
Cookie = maps:get(<<"cookie">>, Result),
?assert(is_binary(Cookie)).4. from_string/1
from_string(String) -> CookieMsg
when
String :: binary() | [binary()],
CookieMsg :: map().Description: Convert a Set-Cookie header string (or list of strings) into a cookie message map. Uses dev_codec_cookie:from/3 and dev_codec_cookie:extract/3 internally.
cookie_from_header_test() ->
Msg = #{ <<"cookie">> => <<"k1=v1; k2=v2">> },
{ok, Parsed} = dev_codec_cookie:from(Msg, #{}, #{}),
{ok, Cookies} = dev_codec_cookie:extract(Parsed, #{}, #{}),
?assertEqual(<<"v1">>, maps:get(<<"k1">>, Cookies)),
?assertEqual(<<"v2">>, maps:get(<<"k2">>, Cookies)).
cookie_from_set_cookie_test() ->
Msg = #{ <<"set-cookie">> => <<"session=abc123; Path=/">> },
{ok, Parsed} = dev_codec_cookie:from(Msg, #{}, #{}),
{ok, Cookies} = dev_codec_cookie:extract(Parsed, #{}, #{}),
Session = maps:get(<<"session">>, Cookies),
?assertEqual(<<"abc123">>, maps:get(<<"value">>, Session)).Test Vector Categories
1. Raw Value Tests
from_string_raw_value =>
{
% All these inputs should parse to the same output
[<<"k1=v1">>, <<"k1=\"v1\"">>],
#{<<"k1">> => <<"v1">>}
}
to_string_raw_value =>
{
% All these should serialize to the same output
[
#{<<"k1">> => <<"v1">>},
#{<<"k1">> => #{<<"value">> => <<"v1">>}},
#{<<"k1">> => #{
<<"value">> => <<"v1">>,
<<"attributes">> => #{},
<<"flags">> => []
}}
],
[<<"k1=\"v1\"">>]
}2. Attribute Tests
from_string_attributes =>
{
[<<"k1=v1; k2=v2">>, <<"k1=\"v1\"; k2=\"v2\"">>],
#{
<<"k1">> => #{
<<"value">> => <<"v1">>,
<<"attributes">> => #{<<"k2">> => <<"v2">>}
}
}
}3. Flag Tests
from_string_flags =>
{
[<<"k1=v1; k2=v2; f1; f2">>, <<"k1=\"v1\"; k2=\"v2\"; f1; f2">>],
#{
<<"k1">> => #{
<<"value">> => <<"v1">>,
<<"attributes">> => #{<<"k2">> => <<"v2">>},
<<"flags">> => [<<"f1">>, <<"f2">>]
}
}
}Testing Strategy
1. Equivalence Class Testing
Test that various input formats parse to the same normalized structure:
% Both quoted and unquoted values parse identically
<<"k1=v1">> ≡ <<"k1=\"v1\"">>
% Both parse to: #{<<"k1">> => <<"v1">>}2. Round-trip Testing
Test that parse → serialize → parse produces identical results.
3. Real-world Scenarios
Tests include real-world cookie patterns:
- Session cookies with SameSite attributes
- JWT tokens in cookies
- Multi-value cookies
- Cookies with special characters
References
- Cookie Codec -
dev_codec_cookie.erl - HTTP Standards - RFC 6265 (HTTP State Management Mechanism)
- EUnit Testing - Erlang unit testing framework
Notes
- Test Module: This is a pure test module with no public exports
- Equivalence Testing: Uses equivalence classes to test parsing robustness
- Normalization: All equivalent inputs normalize to same structure
- Quote Handling: Handles both quoted and unquoted cookie values
- Attribute Support: Tests various cookie attributes (Path, Domain, etc.)
- Flag Support: Tests boolean flags (Secure, HttpOnly, SameSite)
- Coverage: Comprehensive test coverage of cookie codec functionality