diff --git a/DOC/TODO то что пока отложенно на будущее.md b/DOC/TODO то что пока отложенно на будущее.md new file mode 100644 index 0000000..c2b047e --- /dev/null +++ b/DOC/TODO то что пока отложенно на будущее.md @@ -0,0 +1 @@ +Сделать возможность убрать свой лайк. (пока не надо а сложность что надо больше проверок) \ No newline at end of file diff --git a/shine-server-blockchain/src/main/java/blockchain/body/TextBody.java b/shine-server-blockchain/src/main/java/blockchain/body/TextBody.java index 61a4011..df4fe33 100644 --- a/shine-server-blockchain/src/main/java/blockchain/body/TextBody.java +++ b/shine-server-blockchain/src/main/java/blockchain/body/TextBody.java @@ -18,15 +18,15 @@ import java.util.Objects; * [2] ver=1 * * [2] subType (uint16): подтип текстового сообщения - * 1 = новое сообщение (начало ветки) - * 2 = ответ на сообщение (reply) - * 3 = репост (repost) - * 4 = редактирование (edit) + * 1 = новое сообщение (начало ветки) + * 2 = ответ на сообщение (reply) + * 3 = репост (repost) + * 10 = редактирование (edit) <-- ВАЖНО: как на сервере/в БД-триггере * * [2] textLenBytes (uint16) — длина текста в байтах UTF-8 * [N] text UTF-8 * - * Далее ТОЛЬКО если subType == 2 или subType == 3 или subType == 4: + * Далее ТОЛЬКО если subType == 2 или subType == 3 или subType == 10: * [1] toBlockchainNameLen (uint8) * [N] toBlockchainName UTF-8 * [4] toBlockGlobalNumber (int32) @@ -46,9 +46,11 @@ public final class TextBody implements BodyRecord, BodyHasTarget { public static final short SUB_NEW = 1; public static final short SUB_REPLY = 2; public static final short SUB_REPOST = 3; - public static final short SUB_EDIT = 4; - /** Подтип текстового сообщения (1/2/3/4). */ + /** ВАЖНО: EDIT как на сервере (и как ожидает trg_blocks_edit_apply_ai). */ + public static final short SUB_EDIT = 10; + + /** Подтип текстового сообщения (1/2/3/10). */ public final short subType; /** Текст сообщения (строго валидный UTF-8, не пустой/не blank). */ @@ -181,7 +183,7 @@ public final class TextBody implements BodyRecord, BodyHasTarget { this.toBlockHash32 = null; } - /** Сообщение subType=REPLY (2) или subType=REPOST (3) или subType=EDIT (4) со ссылкой на блок. */ + /** Сообщение subType=REPLY (2) или subType=REPOST (3) или subType=EDIT (10) со ссылкой на блок. */ public TextBody(short subType, String message, String toBlockchainName, @@ -303,7 +305,7 @@ public final class TextBody implements BodyRecord, BodyHasTarget { case SUB_NEW -> "NEW (1)"; case SUB_REPLY -> "REPLY (2)"; case SUB_REPOST -> "REPOST (3)"; - case SUB_EDIT -> "EDIT (4)"; + case SUB_EDIT -> "EDIT (10)"; default -> "UNKNOWN"; }; diff --git a/shine-server-db/src/main/java/shine/db/DatabaseInitializer.java b/shine-server-db/src/main/java/shine/db/DatabaseInitializer.java index 9175eb1..96a207e 100644 --- a/shine-server-db/src/main/java/shine/db/DatabaseInitializer.java +++ b/shine-server-db/src/main/java/shine/db/DatabaseInitializer.java @@ -1,6 +1,7 @@ package shine.db; import utils.config.AppConfig; +import utils.config.MsgSubType; import java.io.BufferedReader; import java.io.IOException; @@ -185,6 +186,7 @@ public class DatabaseInitializer { FOREIGN KEY (login) REFERENCES solana_users(login) ); """); + st.executeUpdate(""" CREATE INDEX IF NOT EXISTS idx_blockchain_state_login ON blockchain_state (login); @@ -271,7 +273,7 @@ public class DatabaseInitializer { ON connections_state (login, to_login); """); - // 8) Trigger: connection state (логика та же) + // 8) Trigger: connection state st.executeUpdate(""" CREATE TRIGGER IF NOT EXISTS trg_blocks_connection_state_ai AFTER INSERT ON blocks @@ -288,7 +290,7 @@ public class DatabaseInitializer { NEW.to_bch_name, NEW.to_block_global_number, NEW.to_block_hashe - WHERE NEW.msg_sub_type IN (10, 20, 30) + WHERE NEW.msg_sub_type IN (%d, %d, %d) AND NEW.to_login IS NOT NULL AND NEW.to_bch_name IS NOT NULL ON CONFLICT(login, rel_type, to_login) @@ -301,15 +303,27 @@ public class DatabaseInitializer { WHERE login = NEW.login AND to_login = NEW.to_login AND rel_type = CASE NEW.msg_sub_type - WHEN 11 THEN 10 - WHEN 21 THEN 20 - WHEN 31 THEN 30 + WHEN %d THEN %d + WHEN %d THEN %d + WHEN %d THEN %d ELSE rel_type END - AND NEW.msg_sub_type IN (11, 21, 31); + AND NEW.msg_sub_type IN (%d, %d, %d); END; - """); + """.formatted( + (int) MsgSubType.CONNECTION_FRIEND, + (int) MsgSubType.CONNECTION_FOLLOW, + (int) MsgSubType.CONNECTION_BLOCK, + + (int) MsgSubType.CONNECTION_UNFRIEND, (int) MsgSubType.CONNECTION_FRIEND, + (int) MsgSubType.CONNECTION_UNFOLLOW, (int) MsgSubType.CONNECTION_FOLLOW, + (int) MsgSubType.CONNECTION_UNBLOCK, (int) MsgSubType.CONNECTION_BLOCK, + + (int) MsgSubType.CONNECTION_UNFRIEND, + (int) MsgSubType.CONNECTION_UNFOLLOW, + (int) MsgSubType.CONNECTION_UNBLOCK + )); // 9) message_stats (to_block_hash -> BLOB) st.executeUpdate(""" @@ -341,11 +355,11 @@ public class DatabaseInitializer { ON message_stats (to_login); """); - // 10) Trigger: LIKE (to_block_hashe -> to_block_hash BLOB) + // 10) Trigger: LIKE st.executeUpdate(""" CREATE TRIGGER IF NOT EXISTS trg_blocks_message_stats_like_ai AFTER INSERT ON blocks - WHEN NEW.msg_type = 2 AND NEW.msg_sub_type = 1 + WHEN NEW.msg_type = 2 AND NEW.msg_sub_type = %d BEGIN INSERT INTO message_stats ( to_login, @@ -370,13 +384,13 @@ public class DatabaseInitializer { DO UPDATE SET likes_count = message_stats.likes_count + 1; END; - """); + """.formatted((int) MsgSubType.REACTION_LIKE)); - // 11) Trigger: REPLY (to_block_hashe -> to_block_hash BLOB) + // 11) Trigger: REPLY st.executeUpdate(""" CREATE TRIGGER IF NOT EXISTS trg_blocks_message_stats_reply_ai AFTER INSERT ON blocks - WHEN NEW.msg_type = 1 AND NEW.msg_sub_type = 2 + WHEN NEW.msg_type = 1 AND NEW.msg_sub_type = %d BEGIN INSERT INTO message_stats ( to_login, @@ -401,13 +415,13 @@ public class DatabaseInitializer { DO UPDATE SET replies_count = message_stats.replies_count + 1; END; - """); + """.formatted((int) MsgSubType.TEXT_REPLY)); // 12) Trigger: EDIT — пометить исходный блок st.executeUpdate(""" CREATE TRIGGER IF NOT EXISTS trg_blocks_edit_apply_ai AFTER INSERT ON blocks - WHEN NEW.msg_type = 1 AND NEW.msg_sub_type = 10 + WHEN NEW.msg_type = 1 AND NEW.msg_sub_type = %d BEGIN UPDATE blocks SET edited_by_block_global_number = NEW.block_global_number @@ -415,7 +429,7 @@ public class DatabaseInitializer { AND bch_name = NEW.bch_name AND block_global_number = NEW.to_block_global_number; END; - """); + """.formatted((int) MsgSubType.TEXT_EDIT)); } } } \ No newline at end of file diff --git a/src/test/java/test/it/IT_01_AddUser.java b/src/test/java/test/it/IT_01_AddUser.java index 9d83e30..0b5b3c7 100644 --- a/src/test/java/test/it/IT_01_AddUser.java +++ b/src/test/java/test/it/IT_01_AddUser.java @@ -25,7 +25,6 @@ public class IT_01_AddUser { public static void main(String[] args) { // чтобы тест можно было запускать вообще без JUnit int failed = run(); -// System.exit(failed); } /** Запуск одного теста (standalone). Возвращает 0 если ок, 1 если упал. */ diff --git a/src/test/java/test/it/IT_02_Sessions.java b/src/test/java/test/it/IT_02_Sessions.java index 38619a2..497bda0 100644 --- a/src/test/java/test/it/IT_02_Sessions.java +++ b/src/test/java/test/it/IT_02_Sessions.java @@ -27,7 +27,6 @@ public class IT_02_Sessions { public static void main(String[] args) { ItRunContext.initIfNeeded(); int failed = run(); - System.exit(failed); } /** Запуск одного теста (standalone). Возвращает 0 если ок, 1 если упал. */ diff --git a/src/test/java/test/it/IT_03_AddBlock_NoAuth.java b/src/test/java/test/it/IT_03_AddBlock_NoAuth.java index 0d2ec84..d4ee562 100644 --- a/src/test/java/test/it/IT_03_AddBlock_NoAuth.java +++ b/src/test/java/test/it/IT_03_AddBlock_NoAuth.java @@ -12,7 +12,6 @@ import test.it.utils.*; import utils.crypto.Ed25519Util; import java.time.Duration; -import java.util.Base64; import static org.junit.jupiter.api.Assertions.*; @@ -23,8 +22,8 @@ import static org.junit.jupiter.api.Assertions.*; * чтобы "четвёртый" сценарий гарантированно запускался сразу после "третьего". * * Сценарий: - * 1) AddUser(USER1) 200 или 409 USER_ALREADY_EXISTS - * 2) AddUser(USER2) 200 или 409 USER_ALREADY_EXISTS + * 1) (УБРАНО) AddUser(USER1) — создаётся раньше в первом тесте + * 2) (УБРАНО) AddUser(USER2) — создаётся раньше в первом тесте * * 3) USER1: HEADER + 3 NEW + 2 REPLY + 2 REACT + 3 EDIT (добавили) * - редактируем два ранее написанных сообщения @@ -49,7 +48,6 @@ public class IT_03_AddBlock_NoAuth { public static void main(String[] args) { int failed = run(); -// System.exit(failed); } public static int run() { @@ -69,36 +67,9 @@ public class IT_03_AddBlock_NoAuth { Duration t = Duration.ofSeconds(1); // ========================================================= - // 1) AddUser(USER1) + // USER2 keys (детерминированно из login, как твой ItRunContext) // ========================================================= - 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 блоки (под message_stats + edits) @@ -179,6 +150,7 @@ public class IT_03_AddBlock_NoAuth { ), t); // 3 EDIT (два сообщения исправляем, одно — два раза) + // ВАЖНО: subType EDIT берём из TextBody.SUB_EDIT (единая константа = 10) if (TestConfig.DEBUG()) TestLog.stepTitle("USER1: TEXT#6 (EDIT -> TEXT#2) (исправление #1)"); sender1.send(new TextBody( TextBody.SUB_EDIT, @@ -281,59 +253,4 @@ public class IT_03_AddBlock_NoAuth { 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); - } - } - } } \ No newline at end of file diff --git a/src/test/java/test/it/IT_RunAllMain.java b/src/test/java/test/it/IT_RunAllMain.java index 18b11b5..d3a4fb5 100644 --- a/src/test/java/test/it/IT_RunAllMain.java +++ b/src/test/java/test/it/IT_RunAllMain.java @@ -12,7 +12,6 @@ public class IT_RunAllMain { ItRunContext.initIfNeeded(); int failed = runAll(); - System.exit(failed); } public static int runAll() {