- Rename tests/NATS.Server.Tests -> tests/NATS.Server.Core.Tests - Update solution file, InternalsVisibleTo, and csproj references - Remove JETSTREAM_INTEGRATION_MATRIX and NATS.NKeys from csproj (moved to JetStream.Tests and Auth.Tests) - Update all namespaces from NATS.Server.Tests.* to NATS.Server.Core.Tests.* - Replace private GetFreePort/ReadUntilAsync helpers with TestUtilities calls - Fix stale namespace in Transport.Tests/NetworkingGoParityTests.cs
239 lines
7.3 KiB
C#
239 lines
7.3 KiB
C#
using NATS.Server.Configuration;
|
|
|
|
namespace NATS.Server.Core.Tests;
|
|
|
|
public class NatsConfLexerTests
|
|
{
|
|
[Fact]
|
|
public void Lex_SimpleKeyStringValue_ReturnsKeyAndString()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("foo = \"bar\"").ToList();
|
|
tokens[0].Type.ShouldBe(TokenType.Key);
|
|
tokens[0].Value.ShouldBe("foo");
|
|
tokens[1].Type.ShouldBe(TokenType.String);
|
|
tokens[1].Value.ShouldBe("bar");
|
|
tokens[2].Type.ShouldBe(TokenType.Eof);
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_SingleQuotedString_ReturnsString()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("foo = 'bar'").ToList();
|
|
tokens[1].Type.ShouldBe(TokenType.String);
|
|
tokens[1].Value.ShouldBe("bar");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_IntegerValue_ReturnsInteger()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("port = 4222").ToList();
|
|
tokens[0].Type.ShouldBe(TokenType.Key);
|
|
tokens[0].Value.ShouldBe("port");
|
|
tokens[1].Type.ShouldBe(TokenType.Integer);
|
|
tokens[1].Value.ShouldBe("4222");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_IntegerWithSuffix_ReturnsInteger()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("size = 64mb").ToList();
|
|
tokens[1].Type.ShouldBe(TokenType.Integer);
|
|
tokens[1].Value.ShouldBe("64mb");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_BooleanValues_ReturnsBool()
|
|
{
|
|
foreach (var val in new[] { "true", "false", "yes", "no", "on", "off" })
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize($"flag = {val}").ToList();
|
|
tokens[1].Type.ShouldBe(TokenType.Bool);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_FloatValue_ReturnsFloat()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("rate = 2.5").ToList();
|
|
tokens[1].Type.ShouldBe(TokenType.Float);
|
|
tokens[1].Value.ShouldBe("2.5");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_NegativeNumber_ReturnsInteger()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("offset = -10").ToList();
|
|
tokens[1].Type.ShouldBe(TokenType.Integer);
|
|
tokens[1].Value.ShouldBe("-10");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_DatetimeValue_ReturnsDatetime()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("ts = 2024-01-15T10:30:00Z").ToList();
|
|
tokens[1].Type.ShouldBe(TokenType.DateTime);
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_HashComment_IsIgnored()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("# this is a comment\nfoo = 1").ToList();
|
|
var keys = tokens.Where(t => t.Type == TokenType.Key).ToList();
|
|
keys.Count.ShouldBe(1);
|
|
keys[0].Value.ShouldBe("foo");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_CommentBody_EmitsTextToken()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("# this is a comment\nfoo = 1").ToList();
|
|
var commentBody = tokens.Single(t => t.Type == TokenType.Text);
|
|
commentBody.Value.ShouldBe(" this is a comment");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_SlashComment_IsIgnored()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("// comment\nfoo = 1").ToList();
|
|
var keys = tokens.Where(t => t.Type == TokenType.Key).ToList();
|
|
keys.Count.ShouldBe(1);
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_MapBlock_ReturnsMapStartEnd()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("auth { user: admin }").ToList();
|
|
tokens[0].Type.ShouldBe(TokenType.Key);
|
|
tokens[0].Value.ShouldBe("auth");
|
|
tokens[1].Type.ShouldBe(TokenType.MapStart);
|
|
tokens[2].Type.ShouldBe(TokenType.Key);
|
|
tokens[2].Value.ShouldBe("user");
|
|
tokens[3].Type.ShouldBe(TokenType.String);
|
|
tokens[3].Value.ShouldBe("admin");
|
|
tokens[4].Type.ShouldBe(TokenType.MapEnd);
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_Array_ReturnsArrayStartEnd()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("items = [1, 2, 3]").ToList();
|
|
tokens[1].Type.ShouldBe(TokenType.ArrayStart);
|
|
tokens[2].Type.ShouldBe(TokenType.Integer);
|
|
tokens[2].Value.ShouldBe("1");
|
|
tokens[5].Type.ShouldBe(TokenType.ArrayEnd);
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_Variable_ReturnsVariable()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("secret = $MY_VAR").ToList();
|
|
tokens[1].Type.ShouldBe(TokenType.Variable);
|
|
tokens[1].Value.ShouldBe("MY_VAR");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_Include_ReturnsInclude()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("include \"auth.conf\"").ToList();
|
|
tokens[0].Type.ShouldBe(TokenType.Include);
|
|
tokens[0].Value.ShouldBe("auth.conf");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_EscapeSequences_AreProcessed()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("msg = \"hello\\tworld\\n\"").ToList();
|
|
tokens[1].Type.ShouldBe(TokenType.String);
|
|
tokens[1].Value.ShouldBe("hello\tworld\n");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_HexEscape_IsProcessed()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("val = \"\\x41\\x42\"").ToList();
|
|
tokens[1].Value.ShouldBe("AB");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_ColonSeparator_Works()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("foo: bar").ToList();
|
|
tokens[0].Type.ShouldBe(TokenType.Key);
|
|
tokens[1].Type.ShouldBe(TokenType.String);
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_WhitespaceSeparator_Works()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("foo bar").ToList();
|
|
tokens[0].Type.ShouldBe(TokenType.Key);
|
|
tokens[1].Type.ShouldBe(TokenType.String);
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_SemicolonTerminator_IsHandled()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("foo = 1; bar = 2").ToList();
|
|
var keys = tokens.Where(t => t.Type == TokenType.Key).ToList();
|
|
keys.Count.ShouldBe(2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_EmptyInput_ReturnsEof()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("").ToList();
|
|
tokens.Count.ShouldBe(1);
|
|
tokens[0].Type.ShouldBe(TokenType.Eof);
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_BlockString_ReturnsString()
|
|
{
|
|
var input = "desc (\nthis is\na block\n)\n";
|
|
var tokens = NatsConfLexer.Tokenize(input).ToList();
|
|
tokens[0].Type.ShouldBe(TokenType.Key);
|
|
tokens[1].Type.ShouldBe(TokenType.String);
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_IPAddress_ReturnsString()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("host = 127.0.0.1").ToList();
|
|
tokens[1].Type.ShouldBe(TokenType.String);
|
|
tokens[1].Value.ShouldBe("127.0.0.1");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_TrackLineNumbers()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("a = 1\nb = 2\nc = 3").ToList();
|
|
tokens[0].Line.ShouldBe(1); // a
|
|
tokens[2].Line.ShouldBe(2); // b
|
|
tokens[4].Line.ShouldBe(3); // c
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_UnterminatedString_ReturnsError()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("foo = \"unterminated").ToList();
|
|
tokens.ShouldContain(t => t.Type == TokenType.Error);
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_StringStartingWithDigit_TreatedAsString()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("foo = 3xyz").ToList();
|
|
tokens[1].Type.ShouldBe(TokenType.String);
|
|
tokens[1].Value.ShouldBe("3xyz");
|
|
}
|
|
|
|
[Fact]
|
|
public void Lex_Unicode_surrogate_pairs_in_strings_are_preserved()
|
|
{
|
|
var tokens = NatsConfLexer.Tokenize("msg = \"rocket🚀\"\nport = 4222").ToList();
|
|
tokens[1].Type.ShouldBe(TokenType.String);
|
|
tokens[1].Value.ShouldBe("rocket🚀");
|
|
tokens[2].Line.ShouldBe(2);
|
|
}
|
|
}
|