Сессии работают пользователи добавляются. И тесты красиво выводият коменты. Старый класс теста больше не нужен
This commit is contained in:
AidarKC 2025-12-23 14:00:08 +03:00
parent c515d5287e
commit b5fa05a660
4 changed files with 390 additions and 643 deletions

View File

@ -1,524 +0,0 @@
//package shine.auth;
//
//import com.fasterxml.jackson.databind.JsonNode;
//import com.fasterxml.jackson.databind.ObjectMapper;
//import org.junit.jupiter.api.Assertions;
//import org.junit.jupiter.api.Test;
//import utils.crypto.Ed25519Util;
//
//import java.net.URI;
//import java.net.http.HttpClient;
//import java.net.http.WebSocket;
//import java.nio.charset.StandardCharsets;
//import java.time.Duration;
//import java.util.Base64;
//import java.util.UUID;
//import java.util.concurrent.CompletableFuture;
//import java.util.concurrent.CompletionStage;
//import java.util.concurrent.TimeUnit;
//
///**
// * Интеграционные тесты авторификации по JSON-протоколу через WebSocket.
// *
// * Требуется запущенный сервер на:
// * ws://localhost:7070/ws
// *
// * Операции:
// * - AddUser
// * - AuthChallenge
// * - CreateAuthSession
// * - RefreshSession
// * - CloseActiveSession
// * - (позже) ListSessions
// */
//public class AuthWebSocketIntegrationTest {
//
// private static final String WS_URI = "ws://localhost:7070/ws";
// private static final ObjectMapper JSON = new ObjectMapper();
// private static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient();
//
// /** Таймаут ожидания ответа от сервера в каждом helper-е (секунд). */
// private static final long WS_TIMEOUT_SEC = 15;
//
// // ========================================================================
// // DTO
// // ========================================================================
//
// /** Тестовый пользователь. */
// private static class TestUser {
// String login;
// long loginId;
// long bchId;
//
// byte[] loginPriv;
// byte[] devicePriv;
//
// String loginPubB64;
// String devicePubB64;
// }
//
// /** Токены созданной сессии. */
// private static class SessionTokens {
// String sessionId;
// String sessionPwd;
// String storagePwd;
// }
//
// // ========================================================================
// // ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ
// // ========================================================================
//
// /**
// * Создать тестового пользователя с уникальным логином и ключами Ed25519.
// */
// private TestUser createRandomUser() {
// TestUser u = new TestUser();
//
// long ts = System.currentTimeMillis();
// u.login = "anya_test_auth_scenario_" + ts;
// u.loginId = ts; // просто уникальный long
// u.bchId = ts % 1_000_000; // что-нибудь псевдоуникальное
//
// // Генерируем ключи детерминированно от логина чтобы AddUser и Auth совпадали
// u.loginPriv = Ed25519Util.generatePrivateKeyFromString("login-key-" + u.login);
// u.devicePriv = Ed25519Util.generatePrivateKeyFromString("device-key-" + u.login);
//
// byte[] loginPub = Ed25519Util.derivePublicKey(u.loginPriv);
// byte[] devicePub = Ed25519Util.derivePublicKey(u.devicePriv);
//
// u.loginPubB64 = Ed25519Util.keyToBase64(loginPub);
// u.devicePubB64 = Ed25519Util.keyToBase64(devicePub);
//
// return u;
// }
//
// /**
// * Универсальный helper для одношаговой операции (AddUser, RefreshSession и т.п.).
// * Открывает WebSocket, отправляет JSON, ждёт один ответ.
// *
// * @param requestJson JSON-запрос
// * @param label ярлык для логов
// * @return JsonNode root ответа
// */
// private JsonNode callSingleJsonOp(String requestJson, String label) throws Exception {
// System.out.println();
// System.out.println("===== " + label + " =====");
// System.out.println("📤 Request:");
// System.out.println(requestJson);
//
// CompletableFuture<JsonNode> future = new CompletableFuture<>();
//
// HTTP_CLIENT.newWebSocketBuilder()
// .connectTimeout(Duration.ofSeconds(WS_TIMEOUT_SEC))
// .buildAsync(URI.create(WS_URI), new WebSocket.Listener() {
//
// @Override
// public void onOpen(WebSocket webSocket) {
// webSocket.request(1);
// webSocket.sendText(requestJson, true);
// }
//
// @Override
// public CompletionStage<?> onText(WebSocket webSocket,
// CharSequence data,
// boolean last) {
// String msg = data.toString();
// System.out.println("📥 Response:");
// System.out.println(msg);
// System.out.println("----------------------------------------");
// try {
// JsonNode root = JSON.readTree(msg);
// future.complete(root);
// } catch (Exception e) {
// future.completeExceptionally(e);
// } finally {
// try {
// webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "test done");
// } catch (Exception ignored) {
// }
// }
// webSocket.request(1);
// return CompletableFuture.completedFuture(null);
// }
//
// @Override
// public void onError(WebSocket webSocket, Throwable error) {
// if (!future.isDone()) {
// future.completeExceptionally(error);
// }
// }
//
// @Override
// public CompletionStage<?> onClose(WebSocket webSocket,
// int statusCode,
// String reason) {
// if (!future.isDone()) {
// future.completeExceptionally(new IllegalStateException(
// "WebSocket closed before response. code=" + statusCode + ", reason=" + reason));
// }
// return CompletableFuture.completedFuture(null);
// }
// });
//
// return future.get(WS_TIMEOUT_SEC, TimeUnit.SECONDS);
// }
//
// /**
// * Helper для двухшагового сценария:
// * 1) AuthChallenge
// * 2) CreateAuthSession
// */
// private SessionTokens createSessionForUser(TestUser user, String logPrefix) throws Exception {
// System.out.println();
// System.out.println("===== " + logPrefix + " createSessionForUser: " + user.login + " =====");
//
// CompletableFuture<SessionTokens> resultFuture = new CompletableFuture<>();
//
// HTTP_CLIENT.newWebSocketBuilder()
// .connectTimeout(Duration.ofSeconds(WS_TIMEOUT_SEC))
// .buildAsync(URI.create(WS_URI), new WebSocket.Listener() {
//
// int step = 0;
// WebSocket ws;
// String currentAuthNonce;
//
// @Override
// public void onOpen(WebSocket webSocket) {
// this.ws = webSocket;
// webSocket.request(1);
//
// // Шаг 1: AuthChallenge
// String reqId = "auth-challenge-" + UUID.randomUUID();
// String json = """
// {
// "op": "AuthChallenge",
// "requestId": "%s",
// "payload": {
// "login": "%s"
// }
// }
// """.formatted(reqId, user.login);
//
// System.out.println();
// System.out.println(logPrefix + " 📤 [STEP1 AuthChallenge] Request:");
// System.out.println(json);
// webSocket.sendText(json, true);
// }
//
// @Override
// public CompletionStage<?> onText(WebSocket webSocket,
// CharSequence data,
// boolean last) {
// String msg = data.toString();
// System.out.println();
// System.out.println(logPrefix + " 📥 Incoming message (step " + step + "):");
// System.out.println(msg);
// System.out.println("--------------------------------------------------");
//
// try {
// if (step == 0) {
// // Ответ на AuthChallenge
// JsonNode root = JSON.readTree(msg);
// int status = root.path("status").asInt();
// if (status != 200) {
// String code = root.path("payload").path("code").asText();
// String message = root.path("payload").path("message").asText();
// throw new IllegalStateException(
// "AuthChallenge failed: status=" + status +
// ", code=" + code +
// ", message=" + message);
// }
//
// currentAuthNonce = root.path("payload").path("authNonce").asText(null);
// if (currentAuthNonce == null || currentAuthNonce.isBlank()) {
// throw new IllegalStateException("AuthChallenge: empty authNonce in response");
// }
// System.out.println(logPrefix + " 🔑 authNonce = " + currentAuthNonce);
//
// // Шаг 2: CreateAuthSession
// long timeMs = System.currentTimeMillis();
// String signatureB64 = buildAuthorificatedSignature(
// user.devicePriv,
// currentAuthNonce,
// timeMs
// );
// String storagePwd = generateFakeStoragePwd();
//
// String reqId2 = "create-session-" + UUID.randomUUID();
// String json2 = """
// {
// "op": "CreateAuthSession",
// "requestId": "%s",
// "payload": {
// "storagePwd": "%s",
// "timeMs": %d,
// "signatureB64": "%s",
// "clientInfo": "AuthTestClient/1.0"
// }
// }
// """.formatted(
// reqId2,
// storagePwd,
// timeMs,
// signatureB64
// );
//
// System.out.println();
// System.out.println(logPrefix + " 📤 [STEP2 CreateAuthSession] Request:");
// System.out.println(json2);
//
// step = 1;
// webSocket.sendText(json2, true);
// } else if (step == 1) {
// // Ответ на CreateAuthSession
// JsonNode root = JSON.readTree(msg);
// int status = root.path("status").asInt();
// if (status != 200) {
// String code = root.path("payload").path("code").asText();
// String message = root.path("payload").path("message").asText();
// throw new IllegalStateException(
// "CreateAuthSession failed: status=" + status +
// ", code=" + code +
// ", message=" + message);
// }
//
// String sessionId = root.path("payload").path("sessionId").asText(null);
// String sessionPwd = root.path("payload").path("sessionPwd").asText(null);
// if (sessionId == null || sessionPwd == null) {
// throw new IllegalStateException("CreateAuthSession: sessionId or sessionPwd is null");
// }
//
// SessionTokens tokens = new SessionTokens();
// tokens.sessionId = sessionId;
// tokens.sessionPwd = sessionPwd;
// tokens.storagePwd = null; // мы знаем, какой отправляли, при желании можно сохранить
//
// System.out.println(logPrefix + " 🆔 sessionId = " + sessionId);
// System.out.println(logPrefix + " 🔐 sessionPwd = " + sessionPwd);
//
// resultFuture.complete(tokens);
//
// try {
// webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "session created");
// } catch (Exception ignored) {
// }
// } else {
// // Лишние сообщения считаем ошибкой
// throw new IllegalStateException("Unexpected extra message on step=" + step);
// }
// } catch (Exception ex) {
// if (!resultFuture.isDone()) {
// resultFuture.completeExceptionally(ex);
// }
// try {
// webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "error in test");
// } catch (Exception ignored) {
// }
// } finally {
// webSocket.request(1);
// }
//
// return CompletableFuture.completedFuture(null);
// }
//
// @Override
// public void onError(WebSocket webSocket, Throwable error) {
// System.out.println(logPrefix + " ❌ WebSocket error: " + error.getMessage());
// if (!resultFuture.isDone()) {
// resultFuture.completeExceptionally(error);
// }
// }
//
// @Override
// public CompletionStage<?> onClose(WebSocket webSocket,
// int statusCode,
// String reason) {
// System.out.println(logPrefix + " 🔚 WebSocket closed. code=" + statusCode + ", reason=" + reason);
// if (!resultFuture.isDone()) {
// resultFuture.completeExceptionally(
// new IllegalStateException("Closed before session tokens were received"));
// }
// return CompletableFuture.completedFuture(null);
// }
// });
//
// // ждём результат или ошибку
// return resultFuture.get(WS_TIMEOUT_SEC, TimeUnit.SECONDS);
// }
//
// /**
// * Собрать подпись над строкой "AUTHORIFICATED:" + timeMs + authNonce
// * приватным ключом устройства.
// */
// private String buildAuthorificatedSignature(byte[] devicePrivKey,
// String authNonce,
// long timeMs) {
// String preimageStr = "AUTHORIFICATED:" + timeMs + authNonce;
// byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8);
// byte[] sig = Ed25519Util.sign(preimage, devicePrivKey);
// return Base64.getEncoder().encodeToString(sig);
// }
//
// /** Просто base64 от 32 байт 1..32 — для storagePwd. */
// private String generateFakeStoragePwd() {
// byte[] data = new byte[32];
// for (int i = 0; i < data.length; i++) {
// data[i] = (byte) (i + 1);
// }
// return Base64.getEncoder().encodeToString(data);
// }
//
// // ========================================================================
// // ТЕСТЫ
// // ========================================================================
//
// /**
// * 1) Регистрируем пользователя через AddUser
// */
// @Test
// void addUser_shouldSucceed() throws Exception {
// TestUser user = createRandomUser();
//
// String reqId = "add-" + UUID.randomUUID();
// String json = """
// {
// "op": "AddUser",
// "requestId": "%s",
// "payload": {
// "login": "%s",
// "loginId": %d,
// "bchId": %d,
// "loginKey": "%s",
// "deviceKey": "%s",
// "bchLimit": 1000000
// }
// }
// """.formatted(
// reqId,
// user.login,
// user.loginId,
// user.bchId,
// user.loginPubB64,
// user.devicePubB64
// );
//
// JsonNode resp = callSingleJsonOp(json, "TEST addUser_shouldSucceed");
// int status = resp.path("status").asInt();
// String code = resp.path("payload").path("code").asText(null);
//
// Assertions.assertEquals(
// 200,
// status,
// "Ожидался status=200 при AddUser, но сервер вернул: status=" + status + ", code=" + code
// );
//
// System.out.println("✅ [TEST] AddUser прошёл успешно для login=" + user.login);
// }
//
// /**
// * 2) Создать пользователя и сразу сделать CreateAuthSession (AuthChallenge + CreateAuthSession).
// */
// @Test
// void createSession_flow_shouldReturnSessionIdAndPwd() throws Exception {
// TestUser user = createRandomUser();
//
// // Сначала регистрируем пользователя
// {
// String reqId = "add-" + UUID.randomUUID();
// String json = """
// {
// "op": "AddUser",
// "requestId": "%s",
// "payload": {
// "login": "%s",
// "loginId": %d,
// "bchId": %d,
// "loginKey": "%s",
// "deviceKey": "%s",
// "bchLimit": 1000000
// }
// }
// """.formatted(
// reqId,
// user.login,
// user.loginId,
// user.bchId,
// user.loginPubB64,
// user.devicePubB64
// );
// JsonNode resp = callSingleJsonOp(json, "TEST createSession_flow / AddUser");
// int status = resp.path("status").asInt();
// Assertions.assertEquals(200, status, "AddUser должен вернуть 200");
// }
//
// SessionTokens tokens = createSessionForUser(user, "[createSession_flow]");
// Assertions.assertNotNull(tokens.sessionId, "sessionId не должен быть null");
// Assertions.assertNotNull(tokens.sessionPwd, "sessionPwd не должен быть null");
//
// System.out.println("✅ [TEST] Сессия успешно создана: sessionId=" + tokens.sessionId);
// }
//
// /**
// * 3) Сценарий с двумя сессиями (упрощённая версия):
// * - создаём пользователя
// * - создаём первую сессию
// * - создаём вторую сессию
// * - убеждаемся, что sessionId разные
// *
// * Полный цикл с ListSessions / CloseActiveSession можно будет нарастить поверх
// * этого теста, когда добавим JSON-обработчик Net_ListSessions.
// */
// @Test
// void fullTwoSessionLifecycleScenario() throws Exception {
// TestUser user = createRandomUser();
//
// // 1) AddUser
// String reqId = "add-" + UUID.randomUUID();
// String jsonAdd = """
// {
// "op": "AddUser",
// "requestId": "%s",
// "payload": {
// "login": "%s",
// "loginId": %d,
// "bchId": %d,
// "loginKey": "%s",
// "deviceKey": "%s",
// "bchLimit": 1000000
// }
// }
// """.formatted(
// reqId,
// user.login,
// user.loginId,
// user.bchId,
// user.loginPubB64,
// user.devicePubB64
// );
//
// JsonNode addResp = callSingleJsonOp(jsonAdd, "SCENARIO fullTwoSessionLifecycle / AddUser");
// int addStatus = addResp.path("status").asInt();
// Assertions.assertEquals(200, addStatus, "AddUser должен вернуть 200");
//
// System.out.println("✅ [SC] Пользователь создан: " + user.login);
//
// // 2) Первая сессия
// SessionTokens s1 = createSessionForUser(user, "[SC S1]");
// // 3) Вторая сессия
// SessionTokens s2 = createSessionForUser(user, "[SC S2]");
//
// Assertions.assertNotEquals(
// s1.sessionId,
// s2.sessionId,
// "Первая и вторая сессия должны иметь разные sessionId"
// );
//
// System.out.println();
// System.out.println("✅ [SC] Полный сценарий (упрощённый) успешно отработал:");
// System.out.println(" session1 = " + s1.sessionId);
// System.out.println(" session2 = " + s2.sessionId);
//
// System.out.println("\n ListSessions / CloseActiveSession / повторные проверки можно будет добавить, " +
// "когда JSON-обработчик Net_ListSessions будет реализован на сервере.");
// }
//}

View File

@ -8,38 +8,96 @@ import static org.junit.jupiter.api.Assertions.*;
public class AddUserIT { public class AddUserIT {
// ANSI цвета (работает в большинстве терминалов)
private static final String R = "\u001B[0m";
private static final String G = "\u001B[32m";
private static final String Y = "\u001B[33m";
private static final String RED = "\u001B[31m";
private static final String C = "\u001B[36m";
private static void line() {
System.out.println(C + "------------------------------------------------------------" + R);
}
private static void title(String s) {
System.out.println(C + "\n============================================================" + R);
System.out.println(C + s + R);
System.out.println(C + "============================================================\n" + R);
}
private static void ok(String s) {
System.out.println(G + "" + s + R);
}
private static void warn(String s) {
System.out.println(Y + "⚠️ " + s + R);
}
private static void boom(String s) {
System.out.println(RED + "****************************************************************" + R);
System.out.println(RED + "" + s + R);
System.out.println(RED + "****************************************************************" + R);
}
@Test @Test
void addUser_shouldReturn200_orAlreadyExists() { void addUser_shouldReturn200_orAlreadyExists() {
title("AddUserIT: проверка добавления пользователя (200 OK) или 'уже существует' (409 USER_ALREADY_EXISTS)");
System.out.println("Ожидание:");
System.out.println(" - сервер принимает AddUser и возвращает:");
System.out.println(" * 200 (пользователь создан/добавлен)");
System.out.println(" * либо 409 + payload.code=USER_ALREADY_EXISTS (если уже есть)\n");
try (WsTestClient client = new WsTestClient(TestConfig.WS_URI)) { try (WsTestClient client = new WsTestClient(TestConfig.WS_URI)) {
String reqId = "it-adduser-1"; String reqId = "it-adduser-1";
String reqJson = JsonBuilders.addUser(reqId); String reqJson = JsonBuilders.addUser(reqId);
TestLog.section("AddUserIT: AddUser"); System.out.println("📤 Отправляем AddUser запрос:");
TestLog.req("AddUser requestId=" + reqId, reqJson); System.out.println(reqJson);
line();
String resp = client.request(reqId, reqJson, Duration.ofSeconds(5)); String resp = client.request(reqId, reqJson, Duration.ofSeconds(5));
TestLog.resp("AddUser responseId=" + reqId, resp);
System.out.println("📥 Ответ сервера:");
System.out.println(resp);
line();
int st = JsonParsers.status(resp); int st = JsonParsers.status(resp);
System.out.println(" status=" + st);
boolean created = (st == 200); boolean created = (st == 200);
boolean already = (st == 409); boolean already = (st == 409);
if (already) { if (already) {
// ВАЖНО: payload.code (не errorCode)
String code = JsonParsers.errorCode(resp); String code = JsonParsers.errorCode(resp);
// если сервер кладет code в payload.code парсер должен это поддерживать (см. ниже)
System.out.println(" server_code=" + code);
// Если 409 пришёл, требуем понятный code
try {
assertEquals("USER_ALREADY_EXISTS", code, assertEquals("USER_ALREADY_EXISTS", code,
"Expected errorCode=USER_ALREADY_EXISTS, but got: " + code + ", resp=" + resp); "Expected code=USER_ALREADY_EXISTS, but got: " + code + ", resp=" + resp);
ok("409 получен корректно: USER_ALREADY_EXISTS");
} catch (AssertionError ae) {
boom("409 получен, но code не тот. " + ae.getMessage());
throw ae;
}
} }
if (created) { if (created) {
System.out.println("✅ AddUser: создан/добавлен (status=200)"); ok("ТЕСТ ПРОЙДЕН: AddUser создан/добавлен (status=200)");
} else if (already) { } else if (already) {
System.out.println("✅ AddUser: уже есть в системе (status=409, USER_ALREADY_EXISTS)"); ok("ТЕСТ ПРОЙДЕН: AddUser уже есть в системе (status=409, USER_ALREADY_EXISTS)");
} else { } else {
boom("Неожиданный status=" + st + ", resp=" + resp);
fail("❌ AddUser: неожиданный status=" + st + ", resp=" + resp); fail("❌ AddUser: неожиданный status=" + st + ", resp=" + resp);
} }
} catch (AssertionError | RuntimeException e) {
// чтобы красным было видно даже если Gradle/IDE печатает стек отдельно
boom("ТЕСТ УПАЛ: AddUserIT. Причина: " + e.getMessage());
throw e;
} }
} }
} }

View File

@ -10,15 +10,91 @@ import static org.junit.jupiter.api.Assertions.*;
public class SessionsIT { public class SessionsIT {
// ANSI цвета
private static final String R = "\u001B[0m";
private static final String G = "\u001B[32m";
private static final String Y = "\u001B[33m";
private static final String RED = "\u001B[31m";
private static final String C = "\u001B[36m";
private static void line() {
System.out.println(C + "------------------------------------------------------------" + R);
}
private static void title(String s) {
System.out.println(C + "\n============================================================" + R);
System.out.println(C + s + R);
System.out.println(C + "============================================================\n" + R);
}
private static void stepTitle(String s) {
System.out.println(C + "\n-------------------- " + s + " --------------------" + R);
}
private static void ok(String s) {
System.out.println(G + "" + s + R);
}
private static void warn(String s) {
System.out.println(Y + "⚠️ " + s + R);
}
private static void boom(String s) {
System.out.println(RED + "****************************************************************" + R);
System.out.println(RED + "" + s + R);
System.out.println(RED + "****************************************************************" + R);
}
private static void send(String op, String json) {
System.out.println("📤 [" + op + "] Request JSON:");
System.out.println(json);
line();
}
private static void recv(String op, String json) {
System.out.println("📥 [" + op + "] Response JSON:");
System.out.println(json);
line();
}
private static void assert200(String op, String resp) {
int st = JsonParsers.status(resp);
try {
assertEquals(200, st, op + ": expected status=200, but got=" + st + ", resp=" + resp);
ok(op + ": status=200");
} catch (AssertionError ae) {
boom(op + ": ожидали 200, но получили " + st);
throw ae;
}
}
@BeforeAll @BeforeAll
static void ensureUserExists() { static void ensureUserExists() {
title("SessionsIT (BeforeAll): предусловие — пользователь должен существовать (AddUser: 200 или 409)");
try (WsTestClient client = new WsTestClient(TestConfig.WS_URI)) { try (WsTestClient client = new WsTestClient(TestConfig.WS_URI)) {
String reqId = "it-adduser-beforeall"; String reqId = "it-adduser-beforeall";
String resp = client.request(reqId, JsonBuilders.addUser(reqId), Duration.ofSeconds(5)); String reqJson = JsonBuilders.addUser(reqId);
send("AddUser(BeforeAll)", reqJson);
String resp = client.request(reqId, reqJson, Duration.ofSeconds(5));
recv("AddUser(BeforeAll)", resp);
int st = JsonParsers.status(resp); int st = JsonParsers.status(resp);
// 200 или "уже есть" ок // 200 или "уже есть" ок
if (!(st == 200 || st == 409)) { if (st == 200) {
ok("BeforeAll: пользователь создан/добавлен (status=200)");
} else if (st == 409) {
String code = JsonParsers.errorCode(resp);
if ("USER_ALREADY_EXISTS".equals(code)) {
ok("BeforeAll: пользователь уже есть (status=409, USER_ALREADY_EXISTS)");
} else {
boom("BeforeAll: status=409, но code неожиданный: " + code);
fail("User precondition failed. status=409, code=" + code + ", resp=" + resp);
}
} else {
boom("BeforeAll: предусловие не выполнено. status=" + st);
fail("User precondition failed. status=" + st + ", resp=" + resp); fail("User precondition failed. status=" + st + ", resp=" + resp);
} }
} }
@ -26,93 +102,162 @@ public class SessionsIT {
@Test @Test
void sessions_flow_shouldCreateListRefreshCloseCorrectly() { void sessions_flow_shouldCreateListRefreshCloseCorrectly() {
title("SessionsIT: полный сценарий сессий (создать 2, проверить list, refresh/close, проверить очистку)");
System.out.println("Ожидание сценария:");
System.out.println(" 1) Создаём SESSION1 через AuthChallenge + CreateAuthSession");
System.out.println(" 2) Создаём SESSION2 и делаем ListSessions внутри неё (AUTH_STATUS_USER) → должны быть SESSION1 и SESSION2");
System.out.println(" 3) Делаем ListSessions в AUTH_IN_PROGRESS (подпись по nonce) → должны быть SESSION1 и SESSION2");
System.out.println(" 4) Refresh SESSION1 (входим в AUTH_STATUS_USER) и Close SESSION2");
System.out.println(" 5) Проверяем ListSessions (AUTH_IN_PROGRESS) → осталась только SESSION1");
System.out.println(" 6) Закрываем SESSION1 в AUTH_IN_PROGRESS");
System.out.println(" 7) Проверяем ListSessions → пусто\n");
String s1Id, s1Pwd; String s1Id, s1Pwd;
String s2Id, s2Pwd; String s2Id, s2Pwd;
try {
// --- create session1 --- // --- create session1 ---
stepTitle("ШАГ 1: создать SESSION1 (AuthChallenge -> CreateAuthSession)");
try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) { try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) {
String r1 = "it-auth-1"; String r1 = "it-auth-1";
String resp1 = c.request(r1, JsonBuilders.authChallenge(r1), Duration.ofSeconds(5)); String req1 = JsonBuilders.authChallenge(r1);
assertEquals(200, JsonParsers.status(resp1)); send("AuthChallenge#1", req1);
String resp1 = c.request(r1, req1, Duration.ofSeconds(5));
recv("AuthChallenge#1", resp1);
assert200("AuthChallenge#1", resp1);
String nonce = JsonParsers.authNonce(resp1); String nonce = JsonParsers.authNonce(resp1);
assertNotNull(nonce); assertNotNull(nonce, "AuthChallenge#1: nonce must not be null");
ok("AuthChallenge#1: authNonce получен: " + nonce);
String r2 = "it-create-1"; String r2 = "it-create-1";
String storagePwd = TestConfig.fakeStoragePwd(); String storagePwd = TestConfig.fakeStoragePwd();
String resp2 = c.request(r2, JsonBuilders.createAuthSession(r2, nonce, storagePwd), Duration.ofSeconds(5)); String req2 = JsonBuilders.createAuthSession(r2, nonce, storagePwd);
assertEquals(200, JsonParsers.status(resp2)); send("CreateAuthSession#1", req2);
String resp2 = c.request(r2, req2, Duration.ofSeconds(5));
recv("CreateAuthSession#1", resp2);
assert200("CreateAuthSession#1", resp2);
s1Id = JsonParsers.sessionId(resp2); s1Id = JsonParsers.sessionId(resp2);
s1Pwd = JsonParsers.sessionPwd(resp2); s1Pwd = JsonParsers.sessionPwd(resp2);
assertNotNull(s1Id); assertNotNull(s1Id, "CreateAuthSession#1: sessionId must not be null");
assertNotNull(s1Pwd); assertNotNull(s1Pwd, "CreateAuthSession#1: sessionPwd must not be null");
ok("SESSION1 получена: sessionId=" + s1Id + ", sessionPwd=[получен]");
} }
// --- create session2 and list inside (AUTH_STATUS_USER) --- // --- create session2 and list inside (AUTH_STATUS_USER) ---
stepTitle("ШАГ 2: создать SESSION2 и ListSessions внутри неё (AUTH_STATUS_USER) → должны быть SESSION1+SESSION2");
try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) { try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) {
String r1 = "it-auth-2"; String r1 = "it-auth-2";
String resp1 = c.request(r1, JsonBuilders.authChallenge(r1), Duration.ofSeconds(5)); String req1 = JsonBuilders.authChallenge(r1);
assertEquals(200, JsonParsers.status(resp1)); send("AuthChallenge#2", req1);
String resp1 = c.request(r1, req1, Duration.ofSeconds(5));
recv("AuthChallenge#2", resp1);
assert200("AuthChallenge#2", resp1);
String nonce = JsonParsers.authNonce(resp1); String nonce = JsonParsers.authNonce(resp1);
assertNotNull(nonce); assertNotNull(nonce);
ok("AuthChallenge#2: authNonce получен: " + nonce);
String r2 = "it-create-2"; String r2 = "it-create-2";
String resp2 = c.request(r2, JsonBuilders.createAuthSession(r2, nonce, TestConfig.fakeStoragePwd()), Duration.ofSeconds(5)); String req2 = JsonBuilders.createAuthSession(r2, nonce, TestConfig.fakeStoragePwd());
assertEquals(200, JsonParsers.status(resp2)); send("CreateAuthSession#2", req2);
String resp2 = c.request(r2, req2, Duration.ofSeconds(5));
recv("CreateAuthSession#2", resp2);
assert200("CreateAuthSession#2", resp2);
s2Id = JsonParsers.sessionId(resp2); s2Id = JsonParsers.sessionId(resp2);
s2Pwd = JsonParsers.sessionPwd(resp2); s2Pwd = JsonParsers.sessionPwd(resp2);
assertNotNull(s2Id); assertNotNull(s2Id);
assertNotNull(s2Pwd); assertNotNull(s2Pwd);
ok("SESSION2 получена: sessionId=" + s2Id + ", sessionPwd=[получен]");
// list inside session2 (у тебя это AUTH_STATUS_USER без подписи) // list inside session2 (у тебя это AUTH_STATUS_USER без подписи)
String r3 = "it-list-in-session2"; String r3 = "it-list-in-session2";
String resp3 = c.request(r3, JsonBuilders.listSessions(r3, 0L, ""), Duration.ofSeconds(5)); String req3 = JsonBuilders.listSessions(r3, 0L, "");
assertEquals(200, JsonParsers.status(resp3)); send("ListSessions(in SESSION2)", req3);
String resp3 = c.request(r3, req3, Duration.ofSeconds(5));
recv("ListSessions(in SESSION2)", resp3);
assert200("ListSessions(in SESSION2)", resp3);
List<String> ids = JsonParsers.sessionIds(resp3); List<String> ids = JsonParsers.sessionIds(resp3);
ok("ListSessions(in SESSION2): sessions=" + ids);
assertTrue(ids.contains(s1Id), "Must contain session1"); assertTrue(ids.contains(s1Id), "Must contain session1");
assertTrue(ids.contains(s2Id), "Must contain session2"); assertTrue(ids.contains(s2Id), "Must contain session2");
ok("Проверка OK: список содержит SESSION1 и SESSION2");
} }
// --- list in AUTH_IN_PROGRESS (подпись по nonce) --- // --- list in AUTH_IN_PROGRESS (подпись по nonce) ---
stepTitle("ШАГ 3: ListSessions в AUTH_IN_PROGRESS (nonce+signature) → должны быть SESSION1+SESSION2");
try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) { try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) {
String r1 = "it-auth-list"; String r1 = "it-auth-list";
String resp1 = c.request(r1, JsonBuilders.authChallenge(r1), Duration.ofSeconds(5)); String req1 = JsonBuilders.authChallenge(r1);
assertEquals(200, JsonParsers.status(resp1)); send("AuthChallenge(list)", req1);
String resp1 = c.request(r1, req1, Duration.ofSeconds(5));
recv("AuthChallenge(list)", resp1);
assert200("AuthChallenge(list)", resp1);
String nonce = JsonParsers.authNonce(resp1); String nonce = JsonParsers.authNonce(resp1);
assertNotNull(nonce); assertNotNull(nonce);
ok("AuthChallenge(list): authNonce=" + nonce);
long timeMs = System.currentTimeMillis(); long timeMs = System.currentTimeMillis();
String sig = JsonBuilders.signAuthorificated(nonce, timeMs); String sig = JsonBuilders.signAuthorificated(nonce, timeMs);
ok("Подпись для AUTH_IN_PROGRESS: timeMs=" + timeMs + ", signatureB64=[сгенерирована]");
String r2 = "it-list-auth-in-progress"; String r2 = "it-list-auth-in-progress";
String resp2 = c.request(r2, JsonBuilders.listSessions(r2, timeMs, sig), Duration.ofSeconds(5)); String req2 = JsonBuilders.listSessions(r2, timeMs, sig);
assertEquals(200, JsonParsers.status(resp2)); send("ListSessions(AUTH_IN_PROGRESS)", req2);
String resp2 = c.request(r2, req2, Duration.ofSeconds(5));
recv("ListSessions(AUTH_IN_PROGRESS)", resp2);
assert200("ListSessions(AUTH_IN_PROGRESS)", resp2);
List<String> ids = JsonParsers.sessionIds(resp2); List<String> ids = JsonParsers.sessionIds(resp2);
ok("ListSessions(AUTH_IN_PROGRESS): sessions=" + ids);
assertTrue(ids.contains(s1Id)); assertTrue(ids.contains(s1Id));
assertTrue(ids.contains(s2Id)); assertTrue(ids.contains(s2Id));
ok("Проверка OK: AUTH_IN_PROGRESS список содержит SESSION1 и SESSION2");
} }
// --- refresh session1 and close session2 (from session1) --- // --- refresh session1 and close session2 (from session1) ---
stepTitle("ШАГ 4: Refresh SESSION1 (входим) и Close SESSION2 (из SESSION1)");
try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) { try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) {
String r1 = "it-refresh-s1"; String r1 = "it-refresh-s1";
String resp1 = c.request(r1, JsonBuilders.refreshSession(r1, s1Id, s1Pwd), Duration.ofSeconds(5)); String req1 = JsonBuilders.refreshSession(r1, s1Id, s1Pwd);
assertEquals(200, JsonParsers.status(resp1)); send("RefreshSession(SESSION1)", req1);
String resp1 = c.request(r1, req1, Duration.ofSeconds(5));
recv("RefreshSession(SESSION1)", resp1);
assert200("RefreshSession(SESSION1)", resp1);
assertNotNull(JsonParsers.storagePwd(resp1)); assertNotNull(JsonParsers.storagePwd(resp1));
ok("RefreshSession: storagePwd получен");
String r2 = "it-close-s2"; String r2 = "it-close-s2";
String resp2 = c.request(r2, JsonBuilders.closeActiveSession(r2, s2Id, 0L, ""), Duration.ofSeconds(5)); String req2 = JsonBuilders.closeActiveSession(r2, s2Id, 0L, "");
assertEquals(200, JsonParsers.status(resp2)); send("CloseActiveSession(SESSION2)", req2);
String resp2 = c.request(r2, req2, Duration.ofSeconds(5));
recv("CloseActiveSession(SESSION2)", resp2);
assert200("CloseActiveSession(SESSION2)", resp2);
ok("SESSION2 закрыта");
} }
// --- verify only session1 remains (AUTH_IN_PROGRESS list) --- // --- verify only session1 remains (AUTH_IN_PROGRESS list) ---
stepTitle("ШАГ 5: ListSessions(AUTH_IN_PROGRESS) → должна остаться только SESSION1");
try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) { try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) {
String r1 = "it-auth-list2"; String r1 = "it-auth-list2";
String resp1 = c.request(r1, JsonBuilders.authChallenge(r1), Duration.ofSeconds(5)); String req1 = JsonBuilders.authChallenge(r1);
assertEquals(200, JsonParsers.status(resp1)); send("AuthChallenge(list2)", req1);
String resp1 = c.request(r1, req1, Duration.ofSeconds(5));
recv("AuthChallenge(list2)", resp1);
assert200("AuthChallenge(list2)", resp1);
String nonce = JsonParsers.authNonce(resp1); String nonce = JsonParsers.authNonce(resp1);
assertNotNull(nonce); assertNotNull(nonce);
@ -120,19 +265,31 @@ public class SessionsIT {
String sig = JsonBuilders.signAuthorificated(nonce, timeMs); String sig = JsonBuilders.signAuthorificated(nonce, timeMs);
String r2 = "it-list-after-close-s2"; String r2 = "it-list-after-close-s2";
String resp2 = c.request(r2, JsonBuilders.listSessions(r2, timeMs, sig), Duration.ofSeconds(5)); String req2 = JsonBuilders.listSessions(r2, timeMs, sig);
assertEquals(200, JsonParsers.status(resp2)); send("ListSessions(after close S2)", req2);
String resp2 = c.request(r2, req2, Duration.ofSeconds(5));
recv("ListSessions(after close S2)", resp2);
assert200("ListSessions(after close S2)", resp2);
List<String> ids = JsonParsers.sessionIds(resp2); List<String> ids = JsonParsers.sessionIds(resp2);
ok("ListSessions(after close S2): sessions=" + ids);
assertTrue(ids.contains(s1Id)); assertTrue(ids.contains(s1Id));
assertFalse(ids.contains(s2Id)); assertFalse(ids.contains(s2Id));
ok("Проверка OK: осталась только SESSION1");
} }
// --- close session1 in AUTH_IN_PROGRESS --- // --- close session1 in AUTH_IN_PROGRESS ---
stepTitle("ШАГ 6: Close SESSION1 в AUTH_IN_PROGRESS");
try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) { try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) {
String r1 = "it-auth-close-s1"; String r1 = "it-auth-close-s1";
String resp1 = c.request(r1, JsonBuilders.authChallenge(r1), Duration.ofSeconds(5)); String req1 = JsonBuilders.authChallenge(r1);
assertEquals(200, JsonParsers.status(resp1)); send("AuthChallenge(close S1)", req1);
String resp1 = c.request(r1, req1, Duration.ofSeconds(5));
recv("AuthChallenge(close S1)", resp1);
assert200("AuthChallenge(close S1)", resp1);
String nonce = JsonParsers.authNonce(resp1); String nonce = JsonParsers.authNonce(resp1);
assertNotNull(nonce); assertNotNull(nonce);
@ -140,15 +297,25 @@ public class SessionsIT {
String sig = JsonBuilders.signAuthorificated(nonce, timeMs); String sig = JsonBuilders.signAuthorificated(nonce, timeMs);
String r2 = "it-close-s1"; String r2 = "it-close-s1";
String resp2 = c.request(r2, JsonBuilders.closeActiveSession(r2, s1Id, timeMs, sig), Duration.ofSeconds(5)); String req2 = JsonBuilders.closeActiveSession(r2, s1Id, timeMs, sig);
assertEquals(200, JsonParsers.status(resp2)); send("CloseActiveSession(SESSION1)", req2);
String resp2 = c.request(r2, req2, Duration.ofSeconds(5));
recv("CloseActiveSession(SESSION1)", resp2);
assert200("CloseActiveSession(SESSION1)", resp2);
ok("SESSION1 закрыта");
} }
// --- verify empty list --- // --- verify empty list ---
stepTitle("ШАГ 7: ListSessions(AUTH_IN_PROGRESS) → ожидаем пустой список");
try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) { try (WsTestClient c = new WsTestClient(TestConfig.WS_URI)) {
String r1 = "it-auth-list-empty"; String r1 = "it-auth-list-empty";
String resp1 = c.request(r1, JsonBuilders.authChallenge(r1), Duration.ofSeconds(5)); String req1 = JsonBuilders.authChallenge(r1);
assertEquals(200, JsonParsers.status(resp1)); send("AuthChallenge(list empty)", req1);
String resp1 = c.request(r1, req1, Duration.ofSeconds(5));
recv("AuthChallenge(list empty)", resp1);
assert200("AuthChallenge(list empty)", resp1);
String nonce = JsonParsers.authNonce(resp1); String nonce = JsonParsers.authNonce(resp1);
assertNotNull(nonce); assertNotNull(nonce);
@ -156,11 +323,25 @@ public class SessionsIT {
String sig = JsonBuilders.signAuthorificated(nonce, timeMs); String sig = JsonBuilders.signAuthorificated(nonce, timeMs);
String r2 = "it-list-empty"; String r2 = "it-list-empty";
String resp2 = c.request(r2, JsonBuilders.listSessions(r2, timeMs, sig), Duration.ofSeconds(5)); String req2 = JsonBuilders.listSessions(r2, timeMs, sig);
assertEquals(200, JsonParsers.status(resp2)); send("ListSessions(empty)", req2);
String resp2 = c.request(r2, req2, Duration.ofSeconds(5));
recv("ListSessions(empty)", resp2);
assert200("ListSessions(empty)", resp2);
List<String> ids = JsonParsers.sessionIds(resp2); List<String> ids = JsonParsers.sessionIds(resp2);
ok("ListSessions(empty): sessions=" + ids);
assertTrue(ids.isEmpty(), "Sessions must be empty"); assertTrue(ids.isEmpty(), "Sessions must be empty");
ok("Проверка OK: список пуст");
}
ok("ТЕСТ ПРОЙДЕН ЦЕЛИКОМ: SessionsIT (весь сценарий сессий выполнен успешно)");
} catch (AssertionError | RuntimeException e) {
boom("ТЕСТ УПАЛ: SessionsIT. Причина: " + e.getMessage());
throw e;
} }
} }
} }

View File

@ -0,0 +1,32 @@
package test.it;
public final class TestLog {
private TestLog(){}
// включается так: ./gradlew test -Dit.verbose=true
public static final boolean VERBOSE = true; //Boolean.parseBoolean(System.getProperty("it.verbose", "false"));
public static void info(String s) {
if (VERBOSE) System.out.println(s);
}
public static void section(String title) {
if (!VERBOSE) return;
System.out.println("\n\n==================================================");
System.out.println(title);
System.out.println("==================================================\n");
}
public static void req(String title, String json) {
if (!VERBOSE) return;
System.out.println("\n📤 " + title);
System.out.println(json);
}
public static void resp(String title, String json) {
if (!VERBOSE) return;
System.out.println("\n📥 " + title);
System.out.println(json);
System.out.println("-----------------------------------------------------");
}
}