Skip to content

Commit dbfe57d

Browse files
test: add BuildMissingScopeFixCommands and BuildPairingApprovalFixCommands tests (#145)
Add 15 unit tests covering the two public error-message builder methods on OpenClawGatewayClient that previously had zero test coverage. BuildMissingScopeFixCommands (10 tests): - Null/empty/whitespace scope defaults to 'operator.write' - Specific scope is preserved in output - Empty _grantedOperatorScopes shows '(none reported by gateway)' placeholder - Populated scopes are listed correctly - _operatorDeviceId present vs absent (placeholder shown when missing) - node.* scopes trigger 'node token' warning (case-insensitive match) - Operator-only scopes produce no node-token warning BuildPairingApprovalFixCommands (5 tests): - _operatorDeviceId used when set - Falls back to DeviceIdentity.DeviceId when _operatorDeviceId is null - Empty scopes shows '(none reported by gateway yet)' placeholder - Populated scopes are listed correctly - Output always contains pairing approval instructions Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <[email protected]>
1 parent 4c3466a commit dbfe57d

File tree

1 file changed

+188
-0
lines changed

1 file changed

+188
-0
lines changed

tests/OpenClaw.Shared.Tests/OpenClawGatewayClientTests.cs

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,26 @@ private T GetPrivateField<T>(string fieldName)
227227
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
228228
return (T)(field!.GetValue(_client) ?? throw new InvalidOperationException($"Missing field value: {fieldName}"));
229229
}
230+
231+
public void SetGrantedScopes(string[] scopes) => SetPrivateField("_grantedOperatorScopes", scopes);
232+
233+
public void SetOperatorDeviceId(string? id) => SetPrivateField("_operatorDeviceId", id);
234+
235+
public string CallBuildMissingScopeFixCommands(string missingScope) =>
236+
_client.BuildMissingScopeFixCommands(missingScope);
237+
238+
public string CallBuildPairingApprovalFixCommands() =>
239+
_client.BuildPairingApprovalFixCommands();
240+
241+
public string GetFallbackDeviceId()
242+
{
243+
var identityField = typeof(OpenClawGatewayClient).GetField(
244+
"_deviceIdentity",
245+
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
246+
var identity = identityField!.GetValue(_client)!;
247+
var deviceIdProp = identity.GetType().GetProperty("DeviceId");
248+
return (string)deviceIdProp!.GetValue(identity)!;
249+
}
230250
}
231251

232252
private class TestLogger : IOpenClawLogger
@@ -813,4 +833,172 @@ public void ParseChannelHealth_StatusField_TakesPriorityOverDerivedStatus()
813833
Assert.Single(channels);
814834
Assert.Equal("degraded", channels[0].Status);
815835
}
836+
837+
// ── BuildMissingScopeFixCommands tests ─────────────────────────────────────
838+
839+
[Fact]
840+
public void BuildMissingScopeFixCommands_NullOrEmptyScope_DefaultsToOperatorWrite()
841+
{
842+
var helper = new GatewayClientTestHelper();
843+
844+
var output = helper.CallBuildMissingScopeFixCommands("");
845+
846+
Assert.Contains("Missing scope: operator.write", output);
847+
}
848+
849+
[Fact]
850+
public void BuildMissingScopeFixCommands_WhitespaceScope_DefaultsToOperatorWrite()
851+
{
852+
var helper = new GatewayClientTestHelper();
853+
854+
var output = helper.CallBuildMissingScopeFixCommands(" ");
855+
856+
Assert.Contains("Missing scope: operator.write", output);
857+
}
858+
859+
[Fact]
860+
public void BuildMissingScopeFixCommands_WithSpecificScope_IncludesItInOutput()
861+
{
862+
var helper = new GatewayClientTestHelper();
863+
864+
var output = helper.CallBuildMissingScopeFixCommands("operator.approvals");
865+
866+
Assert.Contains("Missing scope: operator.approvals", output);
867+
}
868+
869+
[Fact]
870+
public void BuildMissingScopeFixCommands_EmptyGrantedScopes_ShowsNoneReportedPlaceholder()
871+
{
872+
var helper = new GatewayClientTestHelper();
873+
// _grantedOperatorScopes is empty by default
874+
875+
var output = helper.CallBuildMissingScopeFixCommands("operator.write");
876+
877+
Assert.Contains("(none reported by gateway)", output);
878+
}
879+
880+
[Fact]
881+
public void BuildMissingScopeFixCommands_WithGrantedScopes_ListsScopesInOutput()
882+
{
883+
var helper = new GatewayClientTestHelper();
884+
helper.SetGrantedScopes(["operator.read", "operator.admin"]);
885+
886+
var output = helper.CallBuildMissingScopeFixCommands("operator.write");
887+
888+
Assert.Contains("operator.read, operator.admin", output);
889+
}
890+
891+
[Fact]
892+
public void BuildMissingScopeFixCommands_WithOperatorDeviceId_IncludesItInOutput()
893+
{
894+
var helper = new GatewayClientTestHelper();
895+
helper.SetOperatorDeviceId("test-device-id-abc123");
896+
897+
var output = helper.CallBuildMissingScopeFixCommands("operator.write");
898+
899+
Assert.Contains("test-device-id-abc123", output);
900+
}
901+
902+
[Fact]
903+
public void BuildMissingScopeFixCommands_NoOperatorDeviceId_ShowsNotReportedPlaceholder()
904+
{
905+
var helper = new GatewayClientTestHelper();
906+
// _operatorDeviceId is null by default
907+
908+
var output = helper.CallBuildMissingScopeFixCommands("operator.write");
909+
910+
Assert.Contains("(not reported for this operator connection)", output);
911+
}
912+
913+
[Fact]
914+
public void BuildMissingScopeFixCommands_WithNodeScopes_ShowsNodeTokenWarning()
915+
{
916+
var helper = new GatewayClientTestHelper();
917+
helper.SetGrantedScopes(["node.read", "node.write"]);
918+
919+
var output = helper.CallBuildMissingScopeFixCommands("operator.write");
920+
921+
Assert.Contains("Detected node.* scopes", output);
922+
Assert.Contains("node token", output);
923+
}
924+
925+
[Fact]
926+
public void BuildMissingScopeFixCommands_WithOnlyOperatorScopes_NoNodeTokenWarning()
927+
{
928+
var helper = new GatewayClientTestHelper();
929+
helper.SetGrantedScopes(["operator.read", "operator.write"]);
930+
931+
var output = helper.CallBuildMissingScopeFixCommands("operator.write");
932+
933+
Assert.DoesNotContain("node token", output);
934+
}
935+
936+
[Fact]
937+
public void BuildMissingScopeFixCommands_NodeScopeIsCaseInsensitive()
938+
{
939+
var helper = new GatewayClientTestHelper();
940+
helper.SetGrantedScopes(["NODE.read"]);
941+
942+
var output = helper.CallBuildMissingScopeFixCommands("operator.write");
943+
944+
Assert.Contains("Detected node.* scopes", output);
945+
}
946+
947+
// ── BuildPairingApprovalFixCommands tests ──────────────────────────────────
948+
949+
[Fact]
950+
public void BuildPairingApprovalFixCommands_WithOperatorDeviceId_UsesItInOutput()
951+
{
952+
var helper = new GatewayClientTestHelper();
953+
helper.SetOperatorDeviceId("operator-device-abc");
954+
955+
var output = helper.CallBuildPairingApprovalFixCommands();
956+
957+
Assert.Contains("operator-device-abc", output);
958+
}
959+
960+
[Fact]
961+
public void BuildPairingApprovalFixCommands_NoOperatorDeviceId_FallsBackToDeviceIdentity()
962+
{
963+
var helper = new GatewayClientTestHelper();
964+
// _operatorDeviceId is null by default
965+
966+
var fallbackId = helper.GetFallbackDeviceId();
967+
var output = helper.CallBuildPairingApprovalFixCommands();
968+
969+
Assert.Contains(fallbackId, output);
970+
}
971+
972+
[Fact]
973+
public void BuildPairingApprovalFixCommands_EmptyGrantedScopes_ShowsNoneYetPlaceholder()
974+
{
975+
var helper = new GatewayClientTestHelper();
976+
// _grantedOperatorScopes is empty by default
977+
978+
var output = helper.CallBuildPairingApprovalFixCommands();
979+
980+
Assert.Contains("(none reported by gateway yet)", output);
981+
}
982+
983+
[Fact]
984+
public void BuildPairingApprovalFixCommands_WithGrantedScopes_ListsThemInOutput()
985+
{
986+
var helper = new GatewayClientTestHelper();
987+
helper.SetGrantedScopes(["operator.read", "operator.pairing"]);
988+
989+
var output = helper.CallBuildPairingApprovalFixCommands();
990+
991+
Assert.Contains("operator.read, operator.pairing", output);
992+
}
993+
994+
[Fact]
995+
public void BuildPairingApprovalFixCommands_ContainsApprovalInstructions()
996+
{
997+
var helper = new GatewayClientTestHelper();
998+
999+
var output = helper.CallBuildPairingApprovalFixCommands();
1000+
1001+
Assert.Contains("pairing required", output);
1002+
Assert.Contains("Approve this Windows tray device ID", output);
1003+
}
8161004
}

0 commit comments

Comments
 (0)