using ZB.MOM.WW.Auth.ApiKeys; namespace ZB.MOM.WW.Auth.ApiKeys.Tests; public class ApiKeyParserTests { // --- basic happy path --- [Fact] public void TryParse_SimpleToken_ReturnsParsedKey() { var result = ApiKeyParser.TryParse("mxgw_alice_SECRET", "mxgw"); Assert.NotNull(result); Assert.Equal("alice", result.KeyId); Assert.Equal("SECRET", result.Secret); } [Fact] public void TryParse_BearerPrefixCaseInsensitive_ReturnsParsedKey() { var result = ApiKeyParser.TryParse("Bearer mxgw_alice_SEC_RET", "mxgw"); Assert.NotNull(result); Assert.Equal("alice", result.KeyId); Assert.Equal("SEC_RET", result.Secret); } [Fact] public void TryParse_BearerLowercase_ReturnsParsedKey() { var result = ApiKeyParser.TryParse("bearer mxgw_alice_SECRET", "mxgw"); Assert.NotNull(result); Assert.Equal("alice", result.KeyId); Assert.Equal("SECRET", result.Secret); } [Fact] public void TryParse_SecretContainsUnderscores_SecretIsEverythingAfterFirstSplit() { var result = ApiKeyParser.TryParse("mxgw_k1_a_b_c", "mxgw"); Assert.NotNull(result); Assert.Equal("k1", result.KeyId); Assert.Equal("a_b_c", result.Secret); } // --- custom prefix --- [Fact] public void TryParse_CustomPrefix_Works() { var result = ApiKeyParser.TryParse("myapp_user42_s3cr3t", "myapp"); Assert.NotNull(result); Assert.Equal("user42", result.KeyId); Assert.Equal("s3cr3t", result.Secret); } [Fact] public void TryParse_CustomPrefix_WithBearer() { var result = ApiKeyParser.TryParse("Bearer myapp_user42_s3cr3t", "myapp"); Assert.NotNull(result); Assert.Equal("user42", result.KeyId); Assert.Equal("s3cr3t", result.Secret); } // --- rejection cases --- [Fact] public void TryParse_WrongPrefix_ReturnsNull() { var result = ApiKeyParser.TryParse("zzz_a_b", "mxgw"); Assert.Null(result); } [Fact] public void TryParse_NoDelimiters_ReturnsNull() { var result = ApiKeyParser.TryParse("nodelims", "mxgw"); Assert.Null(result); } [Fact] public void TryParse_NullInput_ReturnsNull() { var result = ApiKeyParser.TryParse(null, "mxgw"); Assert.Null(result); } [Fact] public void TryParse_EmptyInput_ReturnsNull() { var result = ApiKeyParser.TryParse("", "mxgw"); Assert.Null(result); } [Fact] public void TryParse_WhitespaceInput_ReturnsNull() { var result = ApiKeyParser.TryParse(" ", "mxgw"); Assert.Null(result); } [Fact] public void TryParse_OnlyPrefix_NoKeyIdOrSecret_ReturnsNull() { // "mxgw_" — prefix present but no key id segment var result = ApiKeyParser.TryParse("mxgw_", "mxgw"); Assert.Null(result); } [Fact] public void TryParse_PrefixAndKeyIdButNoSecret_ReturnsNull() { // "mxgw_alice" — no second underscore after key id var result = ApiKeyParser.TryParse("mxgw_alice", "mxgw"); Assert.Null(result); } [Fact] public void TryParse_PrefixAndUnderscoreButEmptySecret_ReturnsNull() { // "mxgw_alice_" — secret is empty var result = ApiKeyParser.TryParse("mxgw_alice_", "mxgw"); Assert.Null(result); } // --- generator↔parser round-trip --- [Fact] public void TryParse_RealGeneratedSecret_RoundTripsKeyIdAndFullSecret() { // ApiKeySecretGenerator produces URL-safe base64 which may contain '_'. // The parser must preserve the full secret even when it contains underscores. string secret = ApiKeySecretGenerator.NewSecret(); string token = $"mxgw_someid_{secret}"; var result = ApiKeyParser.TryParse(token, "mxgw"); Assert.NotNull(result); Assert.Equal("someid", result!.KeyId); Assert.Equal(secret, result.Secret); } }