Add channels IT coverage, live UI loading, and runbook

This commit is contained in:
ai5590
2026-03-30 16:06:28 +03:00
parent 9723696b2c
commit c0fba4af94
10 changed files with 549 additions and 69 deletions
@@ -0,0 +1,96 @@
package test.it.cases;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import test.it.utils.TestConfig;
import test.it.utils.json.JsonBuilders;
import test.it.utils.json.JsonParsers;
import test.it.utils.log.TestResult;
import test.it.utils.ws.WsSession;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.fail;
/**
* IT_06_ChannelsApi
*
* Проверяет базовые happy-path сценарии для новых операций:
* - ListSubscriptionsFeed
* - GetChannelMessages
* - GetMessageThread
*/
public class IT_06_ChannelsApi {
private static final ObjectMapper MAPPER = new ObjectMapper();
public static String run() {
TestResult r = new TestResult("IT_06_ChannelsApi");
Duration t = Duration.ofSeconds(8);
final String login = TestConfig.LOGIN();
final String bchName = TestConfig.getBlockchainName(login);
try (WsSession ws = WsSession.open()) {
String feedResp = ws.call("ListSubscriptionsFeed", JsonBuilders.listSubscriptionsFeed(login, 200), t);
check200(r, feedResp, "ListSubscriptionsFeed");
int ownSize = JsonParsers.payloadArraySize(feedResp, "ownedChannels");
if (ownSize < 0) {
r.fail("ListSubscriptionsFeed: отсутствует ownedChannels array, resp=" + feedResp);
fail("ownedChannels missing");
}
r.ok("ListSubscriptionsFeed: ownedChannels size=" + ownSize);
String chResp = ws.call("GetChannelMessages", JsonBuilders.getChannelMessages(bchName, 0, "", 200, "asc"), t);
check200(r, chResp, "GetChannelMessages");
JsonNode chRoot = MAPPER.readTree(chResp);
JsonNode messages = chRoot.path("payload").path("messages");
if (!messages.isArray()) {
r.fail("GetChannelMessages: payload.messages не массив, resp=" + chResp);
fail("messages is not array");
}
r.ok("GetChannelMessages: messages size=" + messages.size());
if (messages.size() > 0) {
JsonNode first = messages.get(0);
int blockNumber = first.path("messageRef").path("blockNumber").asInt(-1);
String blockHash = first.path("messageRef").path("blockHash").asText("");
if (blockNumber > 0 && !blockHash.isBlank()) {
String threadResp = ws.call(
"GetMessageThread",
JsonBuilders.getMessageThread(bchName, blockNumber, blockHash, 20, 2, 50),
t
);
check200(r, threadResp, "GetMessageThread");
JsonNode threadRoot = MAPPER.readTree(threadResp).path("payload");
if (!threadRoot.path("ancestors").isArray() || !threadRoot.has("focus") || !threadRoot.path("descendants").isArray()) {
r.fail("GetMessageThread: неверная форма payload, resp=" + threadResp);
fail("thread payload shape invalid");
}
r.ok("GetMessageThread: payload shape OK");
} else {
r.ok("GetMessageThread: пропущено, у первого сообщения нет корректного ref");
}
} else {
r.ok("GetMessageThread: пропущено, в канале нет сообщений");
}
} catch (Throwable e) {
r.fail("IT_06_ChannelsApi упал: " + e.getMessage());
}
return r.summaryLine();
}
private static void check200(TestResult r, String resp, String op) {
int st = JsonParsers.status(resp);
if (st != 200) {
r.fail(op + ": ожидали status=200, получили " + st + ", resp=" + resp);
fail(op + " status=" + st);
}
}
}
@@ -6,6 +6,7 @@ import test.it.cases.IT_02_Sessions;
import test.it.cases.IT_03_AddBlock_NoAuth;
import test.it.cases.IT_04_UserParams_NoAuth;
import test.it.cases.IT_05_UserConnections;
import test.it.cases.IT_06_ChannelsApi;
import test.it.utils.log.TestLog;
import java.util.ArrayList;
@@ -56,6 +57,9 @@ public class IT_RunAllMain {
String s5 = IT_05_UserConnections.run(); summaries.add(s5);
if (s5.contains("FAIL:")) { failed++; if (STOP_ON_FIRST_FAIL) return finishEarly(summaries, failed); }
String s6 = IT_06_ChannelsApi.run(); summaries.add(s6);
if (s6.contains("FAIL:")) { failed++; if (STOP_ON_FIRST_FAIL) return finishEarly(summaries, failed); }
return finish(summaries, failed);
}
@@ -255,6 +255,61 @@ public final class JsonBuilders {
""".formatted(requestId, login);
}
public static String listSubscriptionsFeed(String login, int limit) {
String requestId = TestIds.next("subsfeed");
return """
{
"op": "ListSubscriptionsFeed",
"requestId": "%s",
"payload": {
"login": "%s",
"limit": %d
}
}
""".formatted(requestId, login, limit);
}
public static String getChannelMessages(String ownerBlockchainName, int channelRootBlockNumber, String channelRootBlockHash, int limit, String sort) {
String requestId = TestIds.next("chmsg");
String hash = channelRootBlockHash == null ? "" : channelRootBlockHash;
return """
{
"op": "GetChannelMessages",
"requestId": "%s",
"payload": {
"channel": {
"ownerBlockchainName": "%s",
"channelRootBlockNumber": %d,
"channelRootBlockHash": "%s"
},
"limit": %d,
"sort": "%s"
}
}
""".formatted(requestId, ownerBlockchainName, channelRootBlockNumber, hash, limit, sort == null ? "asc" : sort);
}
public static String getMessageThread(String blockchainName, int blockNumber, String blockHash, int depthUp, int depthDown, int limitChildrenPerNode) {
String requestId = TestIds.next("thread");
String hash = blockHash == null ? "" : blockHash;
return """
{
"op": "GetMessageThread",
"requestId": "%s",
"payload": {
"message": {
"blockchainName": "%s",
"blockNumber": %d,
"blockHash": "%s"
},
"depthUp": %d,
"depthDown": %d,
"limitChildrenPerNode": %d
}
}
""".formatted(requestId, blockchainName, blockNumber, hash, depthUp, depthDown, limitChildrenPerNode);
}
/**
* Подпись CreateAuthSession(v2):
* preimage = "AUTH_CREATE_SESSION:" + login + ":" + sessionKey + ":" + storagePwd + ":" + timeMs + ":" + authNonce
@@ -287,4 +287,32 @@ public final class JsonParsers {
return null;
}
}
public static int payloadArraySize(String json, String field) {
try {
JsonNode root = MAPPER.readTree(json);
JsonNode payload = root.get("payload");
if (payload == null) return -1;
JsonNode arr = payload.get(field);
if (arr == null || !arr.isArray()) return -1;
return arr.size();
} catch (Exception e) {
return -1;
}
}
public static int payloadNestedArraySize(String json, String objectField, String arrayField) {
try {
JsonNode root = MAPPER.readTree(json);
JsonNode payload = root.get("payload");
if (payload == null) return -1;
JsonNode obj = payload.get(objectField);
if (obj == null || !obj.isObject()) return -1;
JsonNode arr = obj.get(arrayField);
if (arr == null || !arr.isArray()) return -1;
return arr.size();
} catch (Exception e) {
return -1;
}
}
}