ОГО доделал и тесты (теперь два пользователя добавляется и меж ними есть связи) вроде всё работает
This commit is contained in:
AidarKC 2026-01-02 19:09:17 +03:00
parent c3d20ba338
commit 432b574592
6 changed files with 389 additions and 224 deletions

View File

@ -1,32 +1,49 @@
package test.it;
import blockchain.body.ConnectionBody;
import blockchain.body.HeaderBody;
import blockchain.body.ReactionBody;
import blockchain.body.TextBody;
import blockchain.body.UserParamBody;
import org.junit.jupiter.api.BeforeAll;
import test.it.addBlockUtils.AddBlockSender;
import test.it.addBlockUtils.ChainState;
import test.it.utils.ItRunContext;
import test.it.utils.TestConfig;
import test.it.utils.TestLog;
import test.it.utils.*;
import utils.crypto.Ed25519Util;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Base64;
import static org.junit.jupiter.api.Assertions.*;
/**
* IT_03_AddBlock_NoAuth
*
* Теперь тест максимально "линейный":
* - создаём только Body
* - sender.send(body) делает всё остальное (номера, prev-hash, подпись, отправка, проверка, state)
* ОБЪЕДИНЕНО: прежний IT_03 + прежний IT_04 в одном тесте,
* чтобы "четвёртый" сценарий гарантированно запускался сразу после "третьего".
*
* ДОБАВЛЕНО:
* - 2 reply на старые сообщения (включая reply на reply)
* - ещё 1 реакция (вторая)
* Сценарий:
* 1) AddUser(USER1) 200 или 409 USER_ALREADY_EXISTS
* 2) AddUser(USER2) 200 или 409 USER_ALREADY_EXISTS
*
* 3) USER1: HEADER + 3 NEW + 2 REPLY + 2 REACT (как было)
* 4) USER2: HEADER + UserParams(name+address) + Connection(FRIEND -> USER1)
* 5) USER1: UserParams(name+surname) + Connection(FRIEND -> USER2) + Connection(FOLLOW -> USER2)
*
* Важно:
* - у каждого пользователя СВОЙ ChainState
* - AddBlockSender создаём с новой сигнатурой:
* new AddBlockSender(state, login, blockchainName, loginPrivKey)
* - USER2 ключи делаем детерминированно из login (как в ItRunContext), но локально.
*/
public class IT_03_AddBlock_NoAuth {
// ===== USER2 (константы прямо тут, чтобы не ломать твой TestConfig) =====
private static final String USER2_LOGIN = "Anya2";
private static final String BCH_SUFFIX_3 = "001";
private static final String USER2_BCH = USER2_LOGIN + BCH_SUFFIX_3;
public static void main(String[] args) {
int failed = run();
// System.exit(failed);
@ -39,7 +56,7 @@ public class IT_03_AddBlock_NoAuth {
@BeforeAll
static void ensureUserExists() {
ItRunContext.initIfNeeded();
// как и было: предусловие можно включить потом
// можно оставить пустым, как у тебя
}
private static void testBody() {
@ -48,51 +65,79 @@ public class IT_03_AddBlock_NoAuth {
Duration t = Duration.ofSeconds(1);
// =========================================================
// 1) AddUser(USER1)
// =========================================================
addUserOr409AlreadyExists(
"USER1",
TestConfig.LOGIN(),
TestConfig.BCH_NAME(),
TestConfig.LOGIN_PUBKEY_B64(),
TestConfig.DEVICE_PUBKEY_B64()
);
// =========================================================
// 2) AddUser(USER2)
// =========================================================
// Генерим ключи детерминированно из login (как твой ItRunContext)
byte[] user2LoginPriv = Ed25519Util.generatePrivateKeyFromString(USER2_LOGIN);
byte[] user2LoginPub = Ed25519Util.derivePublicKey(user2LoginPriv);
byte[] user2DevPriv = Ed25519Util.generatePrivateKeyFromString(USER2_LOGIN + "#device");
byte[] user2DevPub = Ed25519Util.derivePublicKey(user2DevPriv);
String user2LoginPubB64 = Base64.getEncoder().encodeToString(user2LoginPub);
String user2DevPubB64 = Base64.getEncoder().encodeToString(user2DevPub);
addUserOr409AlreadyExists(
"USER2",
USER2_LOGIN,
USER2_BCH,
user2LoginPubB64,
user2DevPubB64
);
// =========================================================
// 3) USER1 блоки (как было раньше в IT_03)
// =========================================================
if (TestConfig.DEBUG()) {
TestLog.titleBlock("""
IT_03_AddBlock_NoAuth: AddBlock без отдельной авторизации (Body-only в тесте)
login = %s
blockchainName = %s
""".formatted(TestConfig.LOGIN(), TestConfig.BCH_NAME()));
IT_03_AddBlock_NoAuth (combined): USER1 + USER2 сценарии
USER1 login = %s
USER1 blockchainName= %s
USER2 login = %s
USER2 blockchainName= %s
""".formatted(TestConfig.LOGIN(), TestConfig.BCH_NAME(), USER2_LOGIN, USER2_BCH));
}
ChainState state = new ChainState();
AddBlockSender sender = new AddBlockSender(state);
ChainState st1 = new ChainState();
AddBlockSender sender1 = new AddBlockSender(
st1,
TestConfig.LOGIN(),
TestConfig.BCH_NAME(),
TestConfig.LOGIN_PRIV_KEY()
);
// =========================================================
// 0) HEADER
// =========================================================
if (TestConfig.DEBUG()) TestLog.stepTitle("ШАГ 0: HEADER");
sender.send(new HeaderBody(TestConfig.LOGIN()), t);
assertTrue(state.hasHeader());
if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: HEADER");
sender1.send(new HeaderBody(TestConfig.LOGIN()), t);
assertTrue(st1.hasHeader());
// =========================================================
// 1..3) TEXT NEW
// =========================================================
if (TestConfig.DEBUG()) TestLog.stepTitle("ШАГ 1: TEXT#1 (NEW)");
sender.send(new TextBody(TextBody.SUB_NEW, "Hello #1 (NEW) from IT_03 test"), t);
if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#1 (NEW)");
sender1.send(new TextBody(TextBody.SUB_NEW, "Hello #1 (NEW) from IT_03 test"), t);
if (TestConfig.DEBUG()) TestLog.stepTitle("ШАГ 2: TEXT#2 (NEW)");
sender.send(new TextBody(TextBody.SUB_NEW, "Hello #2 (NEW) from IT_03 test"), t);
if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#2 (NEW)");
sender1.send(new TextBody(TextBody.SUB_NEW, "Hello #2 (NEW) from IT_03 test"), t);
if (TestConfig.DEBUG()) TestLog.stepTitle("ШАГ 3: TEXT#3 (NEW)");
sender.send(new TextBody(TextBody.SUB_NEW, "Hello #3 (NEW) from IT_03 test"), t);
if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#3 (NEW)");
sender1.send(new TextBody(TextBody.SUB_NEW, "Hello #3 (NEW) from IT_03 test"), t);
// Теперь у нас есть:
// global=1 -> TEXT#1
// global=2 -> TEXT#2
// global=3 -> TEXT#3
byte[] text1Hash = state.getGlobalHash32(1);
byte[] text2Hash = state.getGlobalHash32(2);
byte[] text1Hash = st1.getGlobalHash32(1);
byte[] text2Hash = st1.getGlobalHash32(2);
assertNotNull(text1Hash);
assertNotNull(text2Hash);
// =========================================================
// 4) REPLY на TEXT#1
// =========================================================
if (TestConfig.DEBUG()) TestLog.stepTitle("ШАГ 4: TEXT#4 (REPLY -> TEXT#1)");
sender.send(new TextBody(
if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#4 (REPLY -> TEXT#1)");
sender1.send(new TextBody(
TextBody.SUB_REPLY,
"Reply to TEXT#1",
TestConfig.BCH_NAME(),
@ -100,15 +145,11 @@ public class IT_03_AddBlock_NoAuth {
text1Hash
), t);
// global=4 -> REPLY на global=1
byte[] reply1Hash = state.getGlobalHash32(4);
byte[] reply1Hash = st1.getGlobalHash32(4);
assertNotNull(reply1Hash);
// =========================================================
// 5) REPLY на REPLY (ответ на ответ)
// =========================================================
if (TestConfig.DEBUG()) TestLog.stepTitle("ШАГ 5: TEXT#5 (REPLY -> TEXT#4)");
sender.send(new TextBody(
if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#5 (REPLY -> TEXT#4)");
sender1.send(new TextBody(
TextBody.SUB_REPLY,
"Reply to REPLY (TEXT#4)",
TestConfig.BCH_NAME(),
@ -116,39 +157,140 @@ public class IT_03_AddBlock_NoAuth {
reply1Hash
), t);
// =========================================================
// 6) REACTION#1 -> LIKE на TEXT#1
// =========================================================
if (TestConfig.DEBUG()) TestLog.stepTitle("ШАГ 6: REACT#1 (LIKE -> TEXT#1)");
sender.send(new ReactionBody(
if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: REACT#1 (LIKE -> TEXT#1)");
sender1.send(new ReactionBody(
ReactionBody.SUB_LIKE,
TestConfig.BCH_NAME(),
1,
text1Hash
), t);
// =========================================================
// 7) REACTION#2 -> LIKE на REPLY (TEXT#4)
// =========================================================
if (TestConfig.DEBUG()) TestLog.stepTitle("ШАГ 7: REACT#2 (LIKE -> TEXT#4)");
sender.send(new ReactionBody(
if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: REACT#2 (LIKE -> TEXT#4)");
sender1.send(new ReactionBody(
ReactionBody.SUB_LIKE,
TestConfig.BCH_NAME(),
4,
reply1Hash
), t);
// =========================================================
// Итоги: 1 header + 3 new + 2 reply + 2 react = 8 блоков
// globalLastNumber должен быть 7
// =========================================================
assertEquals(7, state.globalLastNumber(), "Должно быть 8 блоков: globalLastNumber=7");
assertEquals(5, state.lineLastNumber((short) 1), "В line=1 должно быть 5 TEXT блоков (3 new + 2 reply)");
assertEquals(2, state.lineLastNumber((short) 2), "В line=2 должно быть 2 REACTION блока");
assertEquals(7, st1.globalLastNumber(), "USER1: должно быть 8 блоков: globalLastNumber=7");
assertEquals(5, st1.lineLastNumber((short) 1), "USER1: line=1 должно быть 5 TEXT блоков (3 new + 2 reply)");
assertEquals(2, st1.lineLastNumber((short) 2), "USER1: line=2 должно быть 2 REACTION блока");
assertNotNull(state.globalLastHashHex());
assertEquals(64, state.globalLastHashHex().length());
// =========================================================
// 4) USER2: HEADER + PARAMS + FRIEND->USER1
// =========================================================
ChainState st2 = new ChainState();
AddBlockSender sender2 = new AddBlockSender(
st2,
USER2_LOGIN,
USER2_BCH,
user2LoginPriv
);
TestLog.pass("IT_03_AddBlock_NoAuth: OK");
if (TestConfig.DEBUG()) TestLog.stepTitle("USER2: HEADER");
sender2.send(new HeaderBody(USER2_LOGIN), t);
assertTrue(st2.hasHeader());
if (TestConfig.DEBUG()) TestLog.stepTitle("USER2: UserParams (name + address)");
sender2.send(new UserParamBody(
"Anya",
"Amsterdam, Example street 10"
), t);
if (TestConfig.DEBUG()) TestLog.stepTitle("USER2: Connection (FRIEND -> USER1)");
sender2.send(new ConnectionBody(
ConnectionBody.SUB_FRIEND,
TestConfig.LOGIN(), // to_login (USER1)
TestConfig.BCH_NAME(), // toBch (USER1 chain)
0,
new byte[32]
), t);
// =========================================================
// 5) USER1: params + взаимность + подписка (без нового HEADER!)
// =========================================================
// ВАЖНО: мы НЕ создаём новый ChainState для USER1, и НЕ шлём header заново.
// Мы продолжаем тем же sender1 и st1, иначе будет пытаться начать цепочку заново.
if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: UserParams (name + surname)");
sender1.send(new UserParamBody(
"Anna",
"Gareeva"
), t);
if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: Connection (FRIEND -> USER2)");
sender1.send(new ConnectionBody(
ConnectionBody.SUB_FRIEND,
USER2_LOGIN,
USER2_BCH,
0,
new byte[32]
), t);
if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: Connection (FOLLOW -> USER2)");
sender1.send(new ConnectionBody(
ConnectionBody.SUB_FOLLOW,
USER2_LOGIN,
USER2_BCH,
0,
new byte[32]
), t);
TestLog.pass("IT_03_AddBlock_NoAuth (combined): OK");
}
// ======================================================================
// helpers
// ======================================================================
private static void addUserOr409AlreadyExists(String label,
String login,
String blockchainName,
String loginPubKeyB64,
String devicePubKeyB64) {
TestLog.title(label + ": AddUser (200 OK) или 409 USER_ALREADY_EXISTS");
TestLog.info(" login = " + login);
TestLog.info(" blockchainName = " + blockchainName);
String reqId = "it-adduser-" + label.toLowerCase();
String reqJson = """
{
"op": "AddUser",
"requestId": "%s",
"payload": {
"login": "%s",
"blockchainName": "%s",
"loginKey": "%s",
"deviceKey": "%s",
"bchLimit": %d
}
}
""".formatted(
reqId,
login,
blockchainName,
loginPubKeyB64,
devicePubKeyB64,
TestConfig.TEST_BCH_LIMIT
);
try (WsTestClient client = new WsTestClient(TestConfig.WS_URI)) {
TestLog.send("AddUser(" + label + ")", reqJson);
String resp = client.request(reqId, reqJson, Duration.ofSeconds(5));
TestLog.recv("AddUser(" + label + ")", resp);
int st = JsonParsers.status(resp);
if (st == 200) {
TestLog.ok(label + ": создан/добавлен (status=200)");
} else if (st == 409) {
String code = JsonParsers.errorCode(resp);
assertEquals("USER_ALREADY_EXISTS", code, label + ": expected USER_ALREADY_EXISTS, resp=" + resp);
TestLog.ok(label + ": уже есть (status=409, USER_ALREADY_EXISTS)");
} else {
fail(label + ": неожиданный status=" + st + ", resp=" + resp);
}
}
}
}

View File

@ -5,19 +5,6 @@ import test.it.utils.TestLog;
/**
* Ручной запуск всех IT тестов БЕЗ JUnit / Suite.
*
* Делает:
* 1) запускает тесты по очереди
* 2) печатает итоговый короткий отчёт
*
* Запуск из IDE:
* Run 'main' этого класса
*
* Запуск из консоли:
* ./gradlew testClasses
* java -cp ... test.it.IT_RunAllMain
*
* (Classpath зависит от твоего Gradle, но в IDE проще всего)
*/
public class IT_RunAllMain {
@ -25,15 +12,9 @@ public class IT_RunAllMain {
ItRunContext.initIfNeeded();
int failed = runAll();
// Удобно для CI: код выхода = число упавших тестов
System.exit(failed);
}
/**
* Основной метод, который возвращает число не пройденных тестов (0 если всё хорошо).
* Его можно вызывать из других раннеров (например, из варианта с очисткой data/).
*/
public static int runAll() {
final int total = 3;
@ -42,23 +23,18 @@ public class IT_RunAllMain {
TestLog.title("IT RUN: запуск всех тестов подряд (без очистки data/)");
// 1) IT_01_AddUser
TestLog.stepTitle("RUN: IT_01_AddUser");
int f1 = IT_01_AddUser.run();
failed += f1; passed += (f1 == 0 ? 1 : 0);
// 2) IT_02_Sessions
TestLog.stepTitle("RUN: IT_02_Sessions");
int f2 = IT_02_Sessions.run();
failed += f2; passed += (f2 == 0 ? 1 : 0);
// 3) IT_03_AddBlock_NoAuth (оставлен как есть, поэтому запускаем через его main)
// Если он упадёт он кинет исключение. Мы перехватим и посчитаем как fail=1.
TestLog.stepTitle("RUN: IT_03_AddBlock_NoAuth (main)");
TestLog.stepTitle("RUN: IT_03_AddBlock_NoAuth (combined 3+4)");
int f3 = IT_03_AddBlock_NoAuth.run();
failed += f3; passed += (f3 == 0 ? 1 : 0);
// Итоговый короткий отчёт
TestLog.titleBlock("""
IT RUN RESULT
----------------------------

View File

@ -3,9 +3,7 @@ package test.it.addBlockUtils;
import blockchain.BchBlockEntry;
import blockchain.BchCryptoVerifier;
import blockchain.body.BodyRecord;
import test.it.utils.TestConfig;
import test.it.utils.TestLog;
import utils.crypto.Ed25519Util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@ -17,7 +15,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
/**
* AddBlockSender "одна кнопка":
* - принимает ГОТОВЫЙ Body (HeaderBody/TextBody/ReactionBody)
* - принимает ГОТОВЫЙ Body (HeaderBody/TextBody/ReactionBody/ConnectionBody/UserParamsBody и т.п.)
* - сам берёт номера/prev-hash из ChainState
* - строит raw/hash/signature
* - собирает BchBlockEntry (старый, без изменений)
@ -25,8 +23,10 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
* - проверяет serverLastGlobalHash == localHash
* - обновляет ChainState
*
* В тестах:
* sender.send(body, timeout);
* ИЗМЕНЕНО:
* - sender больше НЕ завязан на TestConfig.LOGIN()/BCH_NAME()/ключи
* - теперь он работает от параметров конкретного пользователя:
* login, blockchainName, loginPrivKey
*/
public final class AddBlockSender {
@ -35,17 +35,22 @@ public final class AddBlockSender {
private final ChainState state;
public AddBlockSender(ChainState state) {
private final String login;
private final String blockchainName;
private final byte[] loginPrivKey;
public AddBlockSender(ChainState state, String login, String blockchainName, byte[] loginPrivKey) {
this.state = state;
this.login = login;
this.blockchainName = blockchainName;
this.loginPrivKey = (loginPrivKey == null ? null : loginPrivKey.clone());
if (this.loginPrivKey == null) throw new IllegalArgumentException("loginPrivKey == null");
}
public ChainState state() {
return state;
}
public ChainState state() { return state; }
/**
* Отправить следующий блок по body.expectedLineIndex().
* Ничего не возвращает состояние хранится в ChainState.
*/
public void send(BodyRecord body, Duration timeout) {
if (body == null) throw new IllegalArgumentException("body == null");
@ -70,10 +75,9 @@ public final class AddBlockSender {
byte[] prevLineHash32 = (lineIndex == 0) ? ZERO32 : state.prevLineHash32ForNext(lineIndex);
long ts = System.currentTimeMillis() / 1000L;
byte[] bodyBytes = body.toBytes();
// RAW bytes (ровно то, что подписываем/хэшируем)
// RAW bytes
int recordSize = BchBlockEntry.RAW_HEADER_SIZE + bodyBytes.length;
byte[] rawBytes = ByteBuffer.allocate(recordSize)
@ -88,13 +92,13 @@ public final class AddBlockSender {
// preimage -> sha256 -> signature
byte[] preimage = BchCryptoVerifier.buildPreimage(
TestConfig.LOGIN(),
login,
prevGlobalHash32,
prevLineHash32,
rawBytes
);
byte[] hash32 = BchCryptoVerifier.sha256(preimage);
byte[] signature64 = Ed25519Util.sign(hash32, TestConfig.LOGIN_PRIV_KEY());
byte[] signature64 = utils.crypto.Ed25519Util.sign(hash32, loginPrivKey);
// Собираем полный блок (BchBlockEntry не меняем)
BchBlockEntry entry = new BchBlockEntry(
@ -107,28 +111,30 @@ public final class AddBlockSender {
hash32
);
// отправляем JSON
// JSON AddBlock
String prevGlobalHashHex = (globalNumber == 0) ? ZERO64 : state.globalLastHashHex();
String req = buildAddBlockJson(
TestConfig.BCH_NAME(),
blockchainName,
globalNumber,
prevGlobalHashHex,
base64(entry.toBytes())
);
String op = "AddBlock (global=" + globalNumber + ", line=" + lineIndex + ", lineNum=" + lineNumber + ")";
String op = "AddBlock (user=" + login + ", bch=" + blockchainName +
", global=" + globalNumber + ", line=" + lineIndex + ", lineNum=" + lineNumber + ")";
String resp = WsJsonOneShot.request(op, req, timeout);
assert200(op, resp);
String serverLastGlobalHash = extractPayloadString(resp, "serverLastGlobalHash");
String serverLastGlobalHash = JsonMini.extractPayloadString(resp, "serverLastGlobalHash");
assertNotNull(serverLastGlobalHash, op + ": payload.serverLastGlobalHash must not be null");
assertEquals(64, serverLastGlobalHash.trim().length(), op + ": serverLastGlobalHash must be 64 hex chars");
String localHashHex = bytesToHex64(hash32);
if (TestConfig.DEBUG()) {
if (test.it.utils.TestConfig.DEBUG()) {
TestLog.ok(op + ": localHash=" + localHashHex);
TestLog.ok(op + ": serverLastGlobalHash=" + serverLastGlobalHash);
}
@ -138,7 +144,7 @@ public final class AddBlockSender {
// обновляем ChainState
state.applyAppendedBlock(globalNumber, lineIndex, lineNumber, hash32);
if (TestConfig.DEBUG()) {
if (test.it.utils.TestConfig.DEBUG()) {
TestLog.ok(op + ": state updated");
}
}
@ -166,17 +172,7 @@ public final class AddBlockSender {
private static void assert200(String op, String resp) {
int st = test.it.utils.JsonParsers.status(resp);
assertEquals(200, st, op + ": expected status=200, but got=" + st + ", resp=" + resp);
if (TestConfig.DEBUG()) TestLog.ok(op + ": status=200");
}
private static String extractPayloadString(String json, String field) {
try {
com.fasterxml.jackson.databind.JsonNode root =
new com.fasterxml.jackson.databind.ObjectMapper().readTree(json);
com.fasterxml.jackson.databind.JsonNode payload = root.get("payload");
if (payload != null && payload.has(field)) return payload.get(field).asText();
} catch (Exception ignore) {}
return null;
if (test.it.utils.TestConfig.DEBUG()) TestLog.ok(op + ": status=200");
}
private static String base64(byte[] bytes) {

View File

@ -5,91 +5,104 @@ import utils.crypto.Ed25519Util;
/**
* Глобальный контекст IT прогона (одна JVM).
*
* ТЕПЕРЬ:
* - login берётся из TestConfig.LOGIN()
* - blockchainName = TestConfig.BCH_NAME()
* - ключи генерятся ДЕТЕРМИНИРОВАННО из login (как ты хотела)
* БЫЛО:
* - один пользователь (login/device)
*
* ПЛЮС:
* - тесты можно запускать по одному initIfNeeded() вызовется автоматически.
* СТАЛО:
* - два пользователя (login1/device1 и login2/device2)
* - ключи детерминированы из логинов
*/
public final class ItRunContext {
private static final Object LOCK = new Object();
private static volatile boolean inited = false;
private static String login;
private static String blockchainName;
private static String login1;
private static String bchName1;
private static byte[] loginPrivKey;
private static byte[] loginPubKey;
private static String login2;
private static String bchName2;
private static byte[] devicePrivKey;
private static byte[] devicePubKey;
private static byte[] login1PrivKey;
private static byte[] login1PubKey;
private static byte[] device1PrivKey;
private static byte[] device1PubKey;
private static byte[] login2PrivKey;
private static byte[] login2PubKey;
private static byte[] device2PrivKey;
private static byte[] device2PubKey;
private ItRunContext() {}
/** Инициализировать, если ещё не инициализировано. */
public static void initIfNeeded() {
if (inited) return;
synchronized (LOCK) {
if (inited) return;
login = TestConfig.LOGIN();
blockchainName = TestConfig.BCH_NAME();
// USER1
login1 = TestConfig.LOGIN();
bchName1 = TestConfig.BCH_NAME();
// 1) Генерация ключей ИЗ login
// loginKey: приватный ключ = SHA-256(login)
loginPrivKey = Ed25519Util.generatePrivateKeyFromString(login);
loginPubKey = Ed25519Util.derivePublicKey(loginPrivKey);
login1PrivKey = Ed25519Util.generatePrivateKeyFromString(login1);
login1PubKey = Ed25519Util.derivePublicKey(login1PrivKey);
// deviceKey: приватный ключ = SHA-256(login + "#device")
String deviceSeedStr = login + "#device";
devicePrivKey = Ed25519Util.generatePrivateKeyFromString(deviceSeedStr);
devicePubKey = Ed25519Util.derivePublicKey(devicePrivKey);
String deviceSeed1 = login1 + "#device";
device1PrivKey = Ed25519Util.generatePrivateKeyFromString(deviceSeed1);
device1PubKey = Ed25519Util.derivePublicKey(device1PrivKey);
// USER2
login2 = TestConfig.LOGIN2();
bchName2 = TestConfig.BCH_NAME2();
login2PrivKey = Ed25519Util.generatePrivateKeyFromString(login2);
login2PubKey = Ed25519Util.derivePublicKey(login2PrivKey);
String deviceSeed2 = login2 + "#device";
device2PrivKey = Ed25519Util.generatePrivateKeyFromString(deviceSeed2);
device2PubKey = Ed25519Util.derivePublicKey(device2PrivKey);
inited = true;
System.out.println(TestColors.C + "\n============================================================" + TestColors.R);
System.out.println(TestColors.C + "IT CONTEXT INIT: фиксированные данные из TestConfig" + TestColors.R);
System.out.println(TestColors.C + "IT CONTEXT INIT: 2 users" + TestColors.R);
System.out.println(TestColors.C + "============================================================" + TestColors.R);
System.out.println("login = " + login);
System.out.println("blockchainName = " + blockchainName);
System.out.println("loginPubKey = " + bytesToHexShort(loginPubKey));
System.out.println("devicePubKey = " + bytesToHexShort(devicePubKey));
System.out.println("USER1 login = " + login1);
System.out.println("USER1 blockchainName = " + bchName1);
System.out.println("USER1 loginPubKey = " + bytesToHexShort(login1PubKey));
System.out.println("USER1 devicePubKey = " + bytesToHexShort(device1PubKey));
System.out.println(TestColors.C + "------------------------------------------------------------" + TestColors.R);
System.out.println("USER2 login = " + login2);
System.out.println("USER2 blockchainName = " + bchName2);
System.out.println("USER2 loginPubKey = " + bytesToHexShort(login2PubKey));
System.out.println("USER2 devicePubKey = " + bytesToHexShort(device2PubKey));
System.out.println(TestColors.C + "------------------------------------------------------------\n" + TestColors.R);
}
}
public static String login() {
initIfNeeded();
return login;
}
// =========================
// USER1 getters
// =========================
public static String login1() { initIfNeeded(); return login1; }
public static String bchName1(){ initIfNeeded(); return bchName1; }
public static String blockchainName() {
initIfNeeded();
return blockchainName;
}
public static byte[] login1PrivKey() { initIfNeeded(); return login1PrivKey.clone(); }
public static byte[] login1PubKey() { initIfNeeded(); return login1PubKey.clone(); }
public static byte[] device1PrivKey(){ initIfNeeded(); return device1PrivKey.clone(); }
public static byte[] device1PubKey() { initIfNeeded(); return device1PubKey.clone(); }
public static byte[] loginPrivKey() {
initIfNeeded();
return loginPrivKey.clone();
}
// =========================
// USER2 getters
// =========================
public static String login2() { initIfNeeded(); return login2; }
public static String bchName2(){ initIfNeeded(); return bchName2; }
public static byte[] loginPubKey() {
initIfNeeded();
return loginPubKey.clone();
}
public static byte[] devicePrivKey() {
initIfNeeded();
return devicePrivKey.clone();
}
public static byte[] devicePubKey() {
initIfNeeded();
return devicePubKey.clone();
}
public static byte[] login2PrivKey() { initIfNeeded(); return login2PrivKey.clone(); }
public static byte[] login2PubKey() { initIfNeeded(); return login2PubKey.clone(); }
public static byte[] device2PrivKey(){ initIfNeeded(); return device2PrivKey.clone(); }
public static byte[] device2PubKey() { initIfNeeded(); return device2PubKey.clone(); }
private static String bytesToHexShort(byte[] b) {
if (b == null) return "null";

View File

@ -8,7 +8,37 @@ import java.util.Base64;
public final class JsonBuilders {
private JsonBuilders(){}
// =========================
// AddUser USER1 (как было)
// =========================
public static String addUser(String requestId) {
return addUserAny(
requestId,
TestConfig.LOGIN(),
TestConfig.BCH_NAME(),
TestConfig.LOGIN_PUBKEY_B64(),
TestConfig.DEVICE_PUBKEY_B64()
);
}
// =========================
// AddUser USER2 (новое)
// =========================
public static String addUser2(String requestId) {
return addUserAny(
requestId,
TestConfig.LOGIN2(),
TestConfig.BCH_NAME2(),
TestConfig.LOGIN2_PUBKEY_B64(),
TestConfig.DEVICE2_PUBKEY_B64()
);
}
private static String addUserAny(String requestId,
String login,
String blockchainName,
String loginKeyB64,
String deviceKeyB64) {
return """
{
"op": "AddUser",
@ -23,10 +53,10 @@ public final class JsonBuilders {
}
""".formatted(
requestId,
TestConfig.LOGIN(),
TestConfig.BCH_NAME(),
TestConfig.LOGIN_PUBKEY_B64(),
TestConfig.DEVICE_PUBKEY_B64(),
login,
blockchainName,
loginKeyB64,
deviceKeyB64,
TestConfig.TEST_BCH_LIMIT
);
}
@ -43,7 +73,7 @@ public final class JsonBuilders {
public static String createAuthSession(String requestId, String authNonce, String storagePwd) {
long timeMs = System.currentTimeMillis();
String sigB64 = signAuthorificated(authNonce, timeMs);
String sigB64 = signAuthorificated(authNonce, timeMs, TestConfig.DEVICE_PRIV_KEY());
return """
{
@ -105,12 +135,17 @@ public final class JsonBuilders {
/**
* Подпись для режима AUTH_IN_PROGRESS:
* preimage = "AUTHORIFICATED:" + timeMs + authNonce
* подписываем devicePrivKey (как в твоём протоколе).
* подписываем devicePrivKey.
*/
public static String signAuthorificated(String authNonce, long timeMs) {
public static String signAuthorificated(String authNonce, long timeMs, byte[] devicePrivKey) {
String preimageStr = "AUTHORIFICATED:" + timeMs + authNonce;
byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8);
byte[] sig = Ed25519Util.sign(preimage, TestConfig.DEVICE_PRIV_KEY());
byte[] sig = Ed25519Util.sign(preimage, devicePrivKey);
return Base64.getEncoder().encodeToString(sig);
}
// старый метод оставим для совместимости
public static String signAuthorificated(String authNonce, long timeMs) {
return signAuthorificated(authNonce, timeMs, TestConfig.DEVICE_PRIV_KEY());
}
}

View File

@ -5,19 +5,11 @@ import java.util.Base64;
/**
* Конфиг для IT тестов.
*
* ЛОГИКА:
* - login по умолчанию берём из DEFAULT_LOGIN
* - можно переопределить запуском:
* -Dit.login=anya24
* -Dit.bchSuffix=001
* ДОБАВЛЕНО:
* - Второй пользователь (LOGIN2) + его blockchainName и ключи.
*
* ВАЖНО:
* - ключи/имя блокчейна вычисляются из login (через ItRunContext).
* - тесты можно запускать по отдельности, ItRunContext сам инициализируется при первом обращении.
*
* ЛОГИ:
* - детальный вывод включается флагом:
* -Dit.debug=true
* Важно:
* - Имена/ключи вычисляются детерминированно из логина (см. ItRunContext).
*/
public final class TestConfig {
@ -26,9 +18,12 @@ public final class TestConfig {
// Твой WS URI
public static final String WS_URI = "ws://localhost:7070/ws";
// ======= По умолчанию (можно поменять под свою среду) =======
// ======= Пользователь #1 (по умолчанию) =======
public static final String DEFAULT_LOGIN = "Anya";
// ======= Пользователь #2 (новый) =======
public static final String DEFAULT_LOGIN2 = "Anya2";
// Суффикс блокчейна по твоему правилу: login + 3 цифры
public static final String DEFAULT_BCH_SUFFIX_3 = "001";
@ -38,51 +33,59 @@ public final class TestConfig {
// Любая строка клиента (для логов)
public static final String TEST_CLIENT_INFO = "it-tests";
/** DEBUG-режим: подробные логи отправки/получения/ожиданий (по умолчанию false). */
/** DEBUG-режим: подробные логи (по умолчанию true, как у тебя). */
public static boolean DEBUG() {
return Boolean.parseBoolean(System.getProperty("it.debug", "true"));
}
/** login для прогона (по умолчанию DEFAULT_LOGIN, можно переопределить -Dit.login=...). */
// =========================
// USER #1
// =========================
/** login для прогона (user1). */
public static String LOGIN() {
return System.getProperty("it.login", DEFAULT_LOGIN);
}
/** Суффикс для имени блокчейна (по умолчанию DEFAULT_BCH_SUFFIX_3, можно переопределить -Dit.bchSuffix=...). */
/** Суффикс для имени блокчейна (user1). */
public static String BCH_SUFFIX_3() {
return System.getProperty("it.bchSuffix", DEFAULT_BCH_SUFFIX_3);
}
/** blockchainName по правилу: login + суффикс. */
/** blockchainName по правилу: login + суффикс (user1). */
public static String BCH_NAME() {
return LOGIN() + BCH_SUFFIX_3();
}
// ======= Ключи (берём из ItRunContext) =======
public static byte[] LOGIN_PRIV_KEY() { return ItRunContext.login1PrivKey(); }
public static byte[] LOGIN_PUB_KEY() { return ItRunContext.login1PubKey(); }
public static byte[] DEVICE_PRIV_KEY(){ return ItRunContext.device1PrivKey(); }
public static byte[] DEVICE_PUB_KEY() { return ItRunContext.device1PubKey(); }
public static byte[] LOGIN_PRIV_KEY() {
return ItRunContext.loginPrivKey();
public static String LOGIN_PUBKEY_B64() { return Base64.getEncoder().encodeToString(LOGIN_PUB_KEY()); }
public static String DEVICE_PUBKEY_B64() { return Base64.getEncoder().encodeToString(DEVICE_PUB_KEY()); }
// =========================
// USER #2
// =========================
/** login второго пользователя. Можно переопределить -Dit.login2=... */
public static String LOGIN2() {
return System.getProperty("it.login2", DEFAULT_LOGIN2);
}
public static byte[] LOGIN_PUB_KEY() {
return ItRunContext.loginPubKey();
/** blockchainName второго: login2 + тот же суффикс. */
public static String BCH_NAME2() {
return LOGIN2() + BCH_SUFFIX_3();
}
public static byte[] DEVICE_PRIV_KEY() {
return ItRunContext.devicePrivKey();
}
public static byte[] LOGIN2_PRIV_KEY() { return ItRunContext.login2PrivKey(); }
public static byte[] LOGIN2_PUB_KEY() { return ItRunContext.login2PubKey(); }
public static byte[] DEVICE2_PRIV_KEY() { return ItRunContext.device2PrivKey(); }
public static byte[] DEVICE2_PUB_KEY() { return ItRunContext.device2PubKey(); }
public static byte[] DEVICE_PUB_KEY() {
return ItRunContext.devicePubKey();
}
public static String LOGIN_PUBKEY_B64() {
return Base64.getEncoder().encodeToString(LOGIN_PUB_KEY());
}
public static String DEVICE_PUBKEY_B64() {
return Base64.getEncoder().encodeToString(DEVICE_PUB_KEY());
}
public static String LOGIN2_PUBKEY_B64() { return Base64.getEncoder().encodeToString(LOGIN2_PUB_KEY()); }
public static String DEVICE2_PUBKEY_B64() { return Base64.getEncoder().encodeToString(DEVICE2_PUB_KEY()); }
/** Псевдо-пароль хранилища — достаточно для тестов. */
public static String fakeStoragePwd() {