28 01 25
Добавил запрос проверить есть ли в системе такой пользователь и получить его данные. И тесты добавил. Все тесты проходят
This commit is contained in:
parent
43b0efb4d3
commit
ebf7c9f18e
@ -101,14 +101,26 @@ public final class DatabaseInitializer {
|
|||||||
st.execute("PRAGMA foreign_keys = ON");
|
st.execute("PRAGMA foreign_keys = ON");
|
||||||
|
|
||||||
// 1. solana_users
|
// 1. solana_users
|
||||||
|
// ВАЖНО:
|
||||||
|
// - Все требуемые поля теперь лежат в solana_users:
|
||||||
|
// login, blockchain_name, solana_key, blockchain_key, device_key
|
||||||
|
// - Поиск по login в DAO сделан case-insensitive.
|
||||||
|
// - Для защиты от дублей "Anya" и "anya" добавляем COLLATE NOCASE на PRIMARY KEY.
|
||||||
st.executeUpdate("""
|
st.executeUpdate("""
|
||||||
CREATE TABLE IF NOT EXISTS solana_users (
|
CREATE TABLE IF NOT EXISTS solana_users (
|
||||||
login TEXT NOT NULL PRIMARY KEY,
|
login TEXT NOT NULL PRIMARY KEY COLLATE NOCASE,
|
||||||
device_key TEXT NOT NULL,
|
blockchain_name TEXT NOT NULL,
|
||||||
solana_key TEXT
|
solana_key TEXT NOT NULL,
|
||||||
|
blockchain_key TEXT NOT NULL,
|
||||||
|
device_key TEXT NOT NULL
|
||||||
);
|
);
|
||||||
""");
|
""");
|
||||||
|
|
||||||
|
st.executeUpdate("""
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS uq_solana_users_blockchain_name
|
||||||
|
ON solana_users (blockchain_name);
|
||||||
|
""");
|
||||||
|
|
||||||
st.executeUpdate("""
|
st.executeUpdate("""
|
||||||
CREATE INDEX IF NOT EXISTS idx_solana_users_login
|
CREATE INDEX IF NOT EXISTS idx_solana_users_login
|
||||||
ON solana_users (login);
|
ON solana_users (login);
|
||||||
|
|||||||
@ -13,9 +13,11 @@ import java.util.List;
|
|||||||
* Таблица: solana_users
|
* Таблица: solana_users
|
||||||
*
|
*
|
||||||
* Колонки:
|
* Колонки:
|
||||||
* - login TEXT PRIMARY KEY
|
* - login TEXT PRIMARY KEY (COLLATE NOCASE)
|
||||||
* - device_key TEXT NOT NULL
|
* - blockchain_name TEXT NOT NULL
|
||||||
* - solana_key TEXT NULLABLE
|
* - solana_key TEXT NOT NULL
|
||||||
|
* - blockchain_key TEXT NOT NULL
|
||||||
|
* - device_key TEXT NOT NULL
|
||||||
*
|
*
|
||||||
* Правило работы с соединениями:
|
* Правило работы с соединениями:
|
||||||
* - методы с Connection НЕ закрывают соединение
|
* - методы с Connection НЕ закрывают соединение
|
||||||
@ -42,14 +44,17 @@ public final class SolanaUsersDAO {
|
|||||||
/** Вставка с внешним соединением. Соединение НЕ закрывает. */
|
/** Вставка с внешним соединением. Соединение НЕ закрывает. */
|
||||||
public void insert(Connection c, SolanaUserEntry user) throws SQLException {
|
public void insert(Connection c, SolanaUserEntry user) throws SQLException {
|
||||||
String sql = """
|
String sql = """
|
||||||
INSERT INTO solana_users (login, device_key, solana_key)
|
INSERT INTO solana_users (
|
||||||
VALUES (?, ?, ?)
|
login, blockchain_name, solana_key, blockchain_key, device_key
|
||||||
|
) VALUES (?, ?, ?, ?, ?)
|
||||||
""";
|
""";
|
||||||
|
|
||||||
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
||||||
ps.setString(1, user.getLogin());
|
ps.setString(1, user.getLogin());
|
||||||
ps.setString(2, user.getDeviceKey());
|
ps.setString(2, user.getBlockchainName());
|
||||||
ps.setString(3, user.getSolanaKey());
|
ps.setString(3, user.getSolanaKey());
|
||||||
|
ps.setString(4, user.getBlockchainKey());
|
||||||
|
ps.setString(5, user.getDeviceKey());
|
||||||
ps.executeUpdate();
|
ps.executeUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,12 +92,41 @@ public final class SolanaUsersDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Проверка существования по blockchain_name (case-sensitive, как в БД) с внешним соединением. */
|
||||||
|
public boolean existsByBlockchainName(Connection c, String blockchainName) throws SQLException {
|
||||||
|
String sql = """
|
||||||
|
SELECT 1
|
||||||
|
FROM solana_users
|
||||||
|
WHERE blockchain_name = ?
|
||||||
|
LIMIT 1
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
||||||
|
ps.setString(1, blockchainName);
|
||||||
|
try (ResultSet rs = ps.executeQuery()) {
|
||||||
|
return rs.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Проверка существования по blockchain_name без внешнего соединения. */
|
||||||
|
public boolean existsByBlockchainName(String blockchainName) throws SQLException {
|
||||||
|
try (Connection c = db.getConnection()) {
|
||||||
|
return existsByBlockchainName(c, blockchainName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------- SELECT --------------------
|
// -------------------- SELECT --------------------
|
||||||
|
|
||||||
/** Получить по login (case-insensitive) с внешним соединением. Соединение НЕ закрывает. */
|
/** Получить по login (case-insensitive) с внешним соединением. Соединение НЕ закрывает. */
|
||||||
public SolanaUserEntry getByLogin(Connection c, String login) throws SQLException {
|
public SolanaUserEntry getByLogin(Connection c, String login) throws SQLException {
|
||||||
String sql = """
|
String sql = """
|
||||||
SELECT login, device_key, solana_key
|
SELECT
|
||||||
|
login,
|
||||||
|
blockchain_name,
|
||||||
|
solana_key,
|
||||||
|
blockchain_key,
|
||||||
|
device_key
|
||||||
FROM solana_users
|
FROM solana_users
|
||||||
WHERE LOWER(login) = LOWER(?)
|
WHERE LOWER(login) = LOWER(?)
|
||||||
""";
|
""";
|
||||||
@ -113,10 +147,44 @@ public final class SolanaUsersDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Получить по blockchain_name (case-sensitive) с внешним соединением. Соединение НЕ закрывает. */
|
||||||
|
public SolanaUserEntry getByBlockchainName(Connection c, String blockchainName) throws SQLException {
|
||||||
|
String sql = """
|
||||||
|
SELECT
|
||||||
|
login,
|
||||||
|
blockchain_name,
|
||||||
|
solana_key,
|
||||||
|
blockchain_key,
|
||||||
|
device_key
|
||||||
|
FROM solana_users
|
||||||
|
WHERE blockchain_name = ?
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
||||||
|
ps.setString(1, blockchainName);
|
||||||
|
try (ResultSet rs = ps.executeQuery()) {
|
||||||
|
if (!rs.next()) return null;
|
||||||
|
return mapRow(rs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Получить по blockchain_name без внешнего соединения. */
|
||||||
|
public SolanaUserEntry getByBlockchainName(String blockchainName) throws SQLException {
|
||||||
|
try (Connection c = db.getConnection()) {
|
||||||
|
return getByBlockchainName(c, blockchainName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Поиск по префиксу с внешним соединением. Соединение НЕ закрывает. */
|
/** Поиск по префиксу с внешним соединением. Соединение НЕ закрывает. */
|
||||||
public List<SolanaUserEntry> searchByLoginPrefix(Connection c, String prefix) throws SQLException {
|
public List<SolanaUserEntry> searchByLoginPrefix(Connection c, String prefix) throws SQLException {
|
||||||
String sql = """
|
String sql = """
|
||||||
SELECT login, device_key, solana_key
|
SELECT
|
||||||
|
login,
|
||||||
|
blockchain_name,
|
||||||
|
solana_key,
|
||||||
|
blockchain_key,
|
||||||
|
device_key
|
||||||
FROM solana_users
|
FROM solana_users
|
||||||
WHERE LOWER(login) LIKE ?
|
WHERE LOWER(login) LIKE ?
|
||||||
ORDER BY login
|
ORDER BY login
|
||||||
@ -145,14 +213,13 @@ public final class SolanaUsersDAO {
|
|||||||
// -------------------- MAPPER --------------------
|
// -------------------- MAPPER --------------------
|
||||||
|
|
||||||
private SolanaUserEntry mapRow(ResultSet rs) throws SQLException {
|
private SolanaUserEntry mapRow(ResultSet rs) throws SQLException {
|
||||||
SolanaUserEntry e = new SolanaUserEntry(
|
SolanaUserEntry e = new SolanaUserEntry();
|
||||||
rs.getString("login"),
|
|
||||||
rs.getString("device_key")
|
|
||||||
);
|
|
||||||
|
|
||||||
String solanaKey = rs.getString("solana_key");
|
e.setLogin(rs.getString("login"));
|
||||||
if (rs.wasNull()) solanaKey = null;
|
e.setBlockchainName(rs.getString("blockchain_name"));
|
||||||
e.setSolanaKey(solanaKey);
|
e.setSolanaKey(rs.getString("solana_key"));
|
||||||
|
e.setBlockchainKey(rs.getString("blockchain_key"));
|
||||||
|
e.setDeviceKey(rs.getString("device_key"));
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,17 @@
|
|||||||
package shine.db.dao;
|
package shine.db.dao;
|
||||||
|
|
||||||
import shine.db.SqliteDbController;
|
import shine.db.SqliteDbController;
|
||||||
import shine.db.entities.BlockchainStateEntry;
|
|
||||||
import shine.db.entities.SolanaUserEntry;
|
import shine.db.entities.SolanaUserEntry;
|
||||||
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UserCreateDAO — атомарное добавление пользователя:
|
* UserCreateDAO — атомарное добавление пользователя:
|
||||||
* - solana_users (login, device_key)
|
* - solana_users (login, blockchain_name, solana_key, blockchain_key, device_key)
|
||||||
* - blockchain_state (blockchain_name, login, blockchain_key, size_limit, ... last_block_number=-1 ...)
|
* - blockchain_state (blockchain_name, login, blockchain_key, size_limit, ... last_block_number=-1 ...)
|
||||||
*
|
*
|
||||||
* ВАЖНО:
|
* ВАЖНО:
|
||||||
* - только INSERT/UPSERT
|
* - только INSERT (без перезаписи существующих записей)
|
||||||
* - если login или blockchainName заняты — возвращаем false (пользователь уже есть/занято)
|
* - если login или blockchainName заняты — возвращаем false (пользователь уже есть/занято)
|
||||||
*/
|
*/
|
||||||
public final class UserCreateDAO {
|
public final class UserCreateDAO {
|
||||||
@ -20,7 +19,6 @@ public final class UserCreateDAO {
|
|||||||
private static volatile UserCreateDAO instance;
|
private static volatile UserCreateDAO instance;
|
||||||
private final SqliteDbController db = SqliteDbController.getInstance();
|
private final SqliteDbController db = SqliteDbController.getInstance();
|
||||||
private final SolanaUsersDAO usersDao = SolanaUsersDAO.getInstance();
|
private final SolanaUsersDAO usersDao = SolanaUsersDAO.getInstance();
|
||||||
private final BlockchainStateDAO stateDao = BlockchainStateDAO.getInstance();
|
|
||||||
|
|
||||||
private UserCreateDAO() {}
|
private UserCreateDAO() {}
|
||||||
|
|
||||||
@ -38,9 +36,10 @@ public final class UserCreateDAO {
|
|||||||
*/
|
*/
|
||||||
public boolean insertUserWithBlockchain(
|
public boolean insertUserWithBlockchain(
|
||||||
String login,
|
String login,
|
||||||
String deviceKey,
|
|
||||||
String blockchainName,
|
String blockchainName,
|
||||||
|
String solanaKey,
|
||||||
String blockchainKey,
|
String blockchainKey,
|
||||||
|
String deviceKey,
|
||||||
long sizeLimit,
|
long sizeLimit,
|
||||||
long nowMs
|
long nowMs
|
||||||
) throws SQLException {
|
) throws SQLException {
|
||||||
@ -55,25 +54,25 @@ public final class UserCreateDAO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1) user
|
// 1) solana_users
|
||||||
SolanaUserEntry u = new SolanaUserEntry(login, deviceKey, deviceKey);
|
SolanaUserEntry u = new SolanaUserEntry();
|
||||||
usersDao.insert(c, u); // если login занят -> constraint
|
u.setLogin(login);
|
||||||
|
u.setBlockchainName(blockchainName);
|
||||||
|
u.setSolanaKey(solanaKey);
|
||||||
|
u.setBlockchainKey(blockchainKey);
|
||||||
|
u.setDeviceKey(deviceKey);
|
||||||
|
|
||||||
// 2) blockchain_state
|
usersDao.insert(c, u); // если login занят (NOCASE) или blockchainName (unique) -> constraint
|
||||||
BlockchainStateEntry st = new BlockchainStateEntry();
|
|
||||||
st.setBlockchainName(blockchainName);
|
|
||||||
st.setLogin(login);
|
|
||||||
st.setBlockchainKey(blockchainKey);
|
|
||||||
st.setSizeLimit(sizeLimit);
|
|
||||||
st.setFileSizeBytes(0L);
|
|
||||||
|
|
||||||
// старт: блоков ещё нет
|
// 2) blockchain_state — строго INSERT, без UPSERT (иначе можно перезаписать существующую цепочку)
|
||||||
st.setLastBlockNumber(-1);
|
insertBlockchainStateStrict(
|
||||||
st.setLastBlockHash(null);
|
c,
|
||||||
|
blockchainName,
|
||||||
st.setUpdatedAtMs(nowMs);
|
login,
|
||||||
|
blockchainKey,
|
||||||
stateDao.upsert(c, st); // если blockchainName занят -> constraint (PK)
|
sizeLimit,
|
||||||
|
nowMs
|
||||||
|
);
|
||||||
|
|
||||||
c.commit();
|
c.commit();
|
||||||
return true;
|
return true;
|
||||||
@ -92,4 +91,43 @@ public final class UserCreateDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void insertBlockchainStateStrict(
|
||||||
|
Connection c,
|
||||||
|
String blockchainName,
|
||||||
|
String login,
|
||||||
|
String blockchainKey,
|
||||||
|
long sizeLimit,
|
||||||
|
long nowMs
|
||||||
|
) throws SQLException {
|
||||||
|
|
||||||
|
String sql = """
|
||||||
|
INSERT INTO blockchain_state (
|
||||||
|
blockchain_name,
|
||||||
|
login,
|
||||||
|
blockchain_key,
|
||||||
|
size_limit,
|
||||||
|
file_size_bytes,
|
||||||
|
last_block_number,
|
||||||
|
last_block_hash,
|
||||||
|
updated_at_ms
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""";
|
||||||
|
|
||||||
|
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
||||||
|
int i = 1;
|
||||||
|
ps.setString(i++, blockchainName);
|
||||||
|
ps.setString(i++, login);
|
||||||
|
ps.setString(i++, blockchainKey);
|
||||||
|
|
||||||
|
ps.setLong(i++, sizeLimit);
|
||||||
|
ps.setLong(i++, 0L);
|
||||||
|
|
||||||
|
ps.setInt(i++, -1);
|
||||||
|
ps.setNull(i++, Types.BLOB); // старт: блоков ещё нет
|
||||||
|
ps.setLong(i++, nowMs);
|
||||||
|
|
||||||
|
ps.executeUpdate(); // если blockchainName занят -> constraint (PK)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -8,38 +8,57 @@ import java.util.Base64;
|
|||||||
* Таблица: solana_users
|
* Таблица: solana_users
|
||||||
*
|
*
|
||||||
* Поля:
|
* Поля:
|
||||||
* - login — PRIMARY KEY (TEXT)
|
* - login — PRIMARY KEY (TEXT) (case-insensitive на уровне COLLATE NOCASE)
|
||||||
* - device_key — TEXT NOT NULL
|
* - blockchain_name — TEXT NOT NULL
|
||||||
* - solana_key — TEXT NULLABLE
|
* - solana_key — TEXT NOT NULL
|
||||||
|
* - blockchain_key — TEXT NOT NULL
|
||||||
|
* - device_key — TEXT NOT NULL
|
||||||
*/
|
*/
|
||||||
public class SolanaUserEntry {
|
public class SolanaUserEntry {
|
||||||
|
|
||||||
private String login;
|
private String login;
|
||||||
private String deviceKey;
|
|
||||||
|
private String blockchainName;
|
||||||
|
|
||||||
|
/** Ключ пользователя Solana (публичный ключ логина) */
|
||||||
private String solanaKey;
|
private String solanaKey;
|
||||||
|
|
||||||
|
/** Ключ блокчейна (публичный ключ блокчейна) */
|
||||||
|
private String blockchainKey;
|
||||||
|
|
||||||
|
/** Ключ устройства (публичный ключ устройства) */
|
||||||
|
private String deviceKey;
|
||||||
|
|
||||||
public SolanaUserEntry() {}
|
public SolanaUserEntry() {}
|
||||||
|
|
||||||
public SolanaUserEntry(String login, String deviceKey) {
|
public SolanaUserEntry(String login,
|
||||||
|
String blockchainName,
|
||||||
|
String solanaKey,
|
||||||
|
String blockchainKey,
|
||||||
|
String deviceKey) {
|
||||||
this.login = login;
|
this.login = login;
|
||||||
this.deviceKey = deviceKey;
|
this.blockchainName = blockchainName;
|
||||||
}
|
|
||||||
|
|
||||||
public SolanaUserEntry(String login, String deviceKey, String solanaKey) {
|
|
||||||
this.login = login;
|
|
||||||
this.deviceKey = deviceKey;
|
|
||||||
this.solanaKey = solanaKey;
|
this.solanaKey = solanaKey;
|
||||||
|
this.blockchainKey = blockchainKey;
|
||||||
|
this.deviceKey = deviceKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLogin() { return login; }
|
public String getLogin() { return login; }
|
||||||
public void setLogin(String login) { this.login = login; }
|
public void setLogin(String login) { this.login = login; }
|
||||||
|
|
||||||
public String getDeviceKey() { return deviceKey; }
|
public String getBlockchainName() { return blockchainName; }
|
||||||
public void setDeviceKey(String deviceKey) { this.deviceKey = deviceKey; }
|
public void setBlockchainName(String blockchainName) { this.blockchainName = blockchainName; }
|
||||||
|
|
||||||
public String getSolanaKey() { return solanaKey; }
|
public String getSolanaKey() { return solanaKey; }
|
||||||
public void setSolanaKey(String solanaKey) { this.solanaKey = solanaKey; }
|
public void setSolanaKey(String solanaKey) { this.solanaKey = solanaKey; }
|
||||||
|
|
||||||
|
public String getBlockchainKey() { return blockchainKey; }
|
||||||
|
public void setBlockchainKey(String blockchainKey) { this.blockchainKey = blockchainKey; }
|
||||||
|
|
||||||
|
public String getDeviceKey() { return deviceKey; }
|
||||||
|
public void setDeviceKey(String deviceKey) { this.deviceKey = deviceKey; }
|
||||||
|
|
||||||
|
// оставляю этот метод как утилиту (иногда удобно), но он работает только для deviceKey:
|
||||||
public byte[] getDeviceKeyByte() {
|
public byte[] getDeviceKeyByte() {
|
||||||
if (deviceKey == null) return null;
|
if (deviceKey == null) return null;
|
||||||
String s = deviceKey.trim();
|
String s = deviceKey.trim();
|
||||||
|
|||||||
@ -28,6 +28,9 @@ import server.logic.ws_protocol.JSON.handlers.blockchain.entyties.Net_AddBlock_R
|
|||||||
import server.logic.ws_protocol.JSON.handlers.tempToTest.Net_AddUser_Handler;
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.Net_AddUser_Handler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_AddUser_Request;
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_AddUser_Request;
|
||||||
|
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.Net_GetUser_Handler;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_GetUser_Request;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.handlers.userParams.Net_GetUserParam_Handler;
|
import server.logic.ws_protocol.JSON.handlers.userParams.Net_GetUserParam_Handler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.userParams.Net_ListUserParams_Handler;
|
import server.logic.ws_protocol.JSON.handlers.userParams.Net_ListUserParams_Handler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.userParams.Net_UpsertUserParam_Handler;
|
import server.logic.ws_protocol.JSON.handlers.userParams.Net_UpsertUserParam_Handler;
|
||||||
@ -50,6 +53,7 @@ public final class JsonHandlerRegistry {
|
|||||||
// Map.of(...) поддерживает максимум 10 пар => используем Map.ofEntries(...)
|
// Map.of(...) поддерживает максимум 10 пар => используем Map.ofEntries(...)
|
||||||
private static final Map<String, JsonMessageHandler> HANDLERS = Map.ofEntries(
|
private static final Map<String, JsonMessageHandler> HANDLERS = Map.ofEntries(
|
||||||
Map.entry("AddUser", new Net_AddUser_Handler()),
|
Map.entry("AddUser", new Net_AddUser_Handler()),
|
||||||
|
Map.entry("GetUser", new Net_GetUser_Handler()),
|
||||||
|
|
||||||
// --- auth ---
|
// --- auth ---
|
||||||
Map.entry("AuthChallenge", new Net_AuthChallenge_Handler()),
|
Map.entry("AuthChallenge", new Net_AuthChallenge_Handler()),
|
||||||
@ -75,6 +79,7 @@ public final class JsonHandlerRegistry {
|
|||||||
|
|
||||||
private static final Map<String, Class<? extends Net_Request>> REQUEST_TYPES = Map.ofEntries(
|
private static final Map<String, Class<? extends Net_Request>> REQUEST_TYPES = Map.ofEntries(
|
||||||
Map.entry("AddUser", Net_AddUser_Request.class),
|
Map.entry("AddUser", Net_AddUser_Request.class),
|
||||||
|
Map.entry("GetUser", Net_GetUser_Request.class),
|
||||||
|
|
||||||
// --- auth ---
|
// --- auth ---
|
||||||
Map.entry("AuthChallenge", Net_AuthChallenge_Request.class),
|
Map.entry("AuthChallenge", Net_AuthChallenge_Request.class),
|
||||||
|
|||||||
@ -61,6 +61,17 @@ public class Net_AddUser_Handler implements JsonMessageHandler {
|
|||||||
: req.getBchLimit();
|
: req.getBchLimit();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// базовая валидация форматов ключей: Base64(32 bytes)
|
||||||
|
byte[] solanaKey32 = Base64.getDecoder().decode(req.getSolanaKey());
|
||||||
|
if (solanaKey32.length != 32) {
|
||||||
|
return NetExceptionResponseFactory.error(
|
||||||
|
req,
|
||||||
|
WireCodes.Status.BAD_REQUEST,
|
||||||
|
"BAD_SOLANA_KEY",
|
||||||
|
"solanaKey должен быть Base64(32 bytes)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
byte[] blockchainKey32 = Base64.getDecoder().decode(req.getBlockchainKey());
|
byte[] blockchainKey32 = Base64.getDecoder().decode(req.getBlockchainKey());
|
||||||
if (blockchainKey32.length != 32) {
|
if (blockchainKey32.length != 32) {
|
||||||
return NetExceptionResponseFactory.error(
|
return NetExceptionResponseFactory.error(
|
||||||
@ -71,6 +82,16 @@ public class Net_AddUser_Handler implements JsonMessageHandler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte[] deviceKey32 = Base64.getDecoder().decode(req.getDeviceKey());
|
||||||
|
if (deviceKey32.length != 32) {
|
||||||
|
return NetExceptionResponseFactory.error(
|
||||||
|
req,
|
||||||
|
WireCodes.Status.BAD_REQUEST,
|
||||||
|
"BAD_DEVICE_KEY",
|
||||||
|
"deviceKey должен быть Base64(32 bytes)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
SolanaUsersDAO usersDAO = SolanaUsersDAO.getInstance();
|
SolanaUsersDAO usersDAO = SolanaUsersDAO.getInstance();
|
||||||
BlockchainStateDAO stateDAO = BlockchainStateDAO.getInstance();
|
BlockchainStateDAO stateDAO = BlockchainStateDAO.getInstance();
|
||||||
|
|
||||||
@ -79,8 +100,8 @@ public class Net_AddUser_Handler implements JsonMessageHandler {
|
|||||||
try (Connection c = db.getConnection()) {
|
try (Connection c = db.getConnection()) {
|
||||||
c.setAutoCommit(false);
|
c.setAutoCommit(false);
|
||||||
|
|
||||||
// 1. Проверяем, что пользователя нет
|
// 1. Проверяем, что пользователя нет (case-insensitive)
|
||||||
if (usersDAO.getByLogin(req.getLogin()) != null) {
|
if (usersDAO.getByLogin(c, req.getLogin()) != null) {
|
||||||
return NetExceptionResponseFactory.error(
|
return NetExceptionResponseFactory.error(
|
||||||
req,
|
req,
|
||||||
409,
|
409,
|
||||||
@ -89,26 +110,38 @@ public class Net_AddUser_Handler implements JsonMessageHandler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Проверяем, что blockchain_state ещё нет
|
// 2. Проверяем, что blockchainName ещё нет (case-sensitive, как в БД)
|
||||||
if (stateDAO.getByBlockchainName(req.getBlockchainName()) != null) {
|
if (usersDAO.existsByBlockchainName(c, req.getBlockchainName())) {
|
||||||
return NetExceptionResponseFactory.error(
|
return NetExceptionResponseFactory.error(
|
||||||
req,
|
req,
|
||||||
409,
|
409,
|
||||||
"BLOCKCHAIN_ALREADY_EXISTS",
|
"BLOCKCHAIN_ALREADY_EXISTS",
|
||||||
|
"Пользователь с таким blockchainName уже существует"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. На всякий случай оставляем старую проверку blockchain_state,
|
||||||
|
// потому что эта таблица нужна серверу (состояние цепочки/лимиты).
|
||||||
|
if (stateDAO.getByBlockchainName(c, req.getBlockchainName()) != null) {
|
||||||
|
return NetExceptionResponseFactory.error(
|
||||||
|
req,
|
||||||
|
409,
|
||||||
|
"BLOCKCHAIN_STATE_ALREADY_EXISTS",
|
||||||
"blockchain_state уже существует"
|
"blockchain_state уже существует"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Создаём пользователя (solanaKey + deviceKey)
|
// 4. Создаём пользователя (все поля теперь лежат в solana_users)
|
||||||
SolanaUserEntry user = new SolanaUserEntry(
|
SolanaUserEntry user = new SolanaUserEntry();
|
||||||
req.getLogin(),
|
user.setLogin(req.getLogin());
|
||||||
req.getSolanaKey(),
|
user.setBlockchainName(req.getBlockchainName());
|
||||||
req.getDeviceKey()
|
user.setSolanaKey(req.getSolanaKey());
|
||||||
);
|
user.setBlockchainKey(req.getBlockchainKey());
|
||||||
|
user.setDeviceKey(req.getDeviceKey());
|
||||||
|
|
||||||
usersDAO.insert(c, user);
|
usersDAO.insert(c, user);
|
||||||
|
|
||||||
// 4. Создаём INITIAL blockchain_state (blockchainKey)
|
// 5. Создаём INITIAL blockchain_state (для работы сервера)
|
||||||
BlockchainStateEntry st = new BlockchainStateEntry();
|
BlockchainStateEntry st = new BlockchainStateEntry();
|
||||||
st.setBlockchainName(req.getBlockchainName());
|
st.setBlockchainName(req.getBlockchainName());
|
||||||
st.setLogin(req.getLogin());
|
st.setLogin(req.getLogin());
|
||||||
|
|||||||
@ -0,0 +1,84 @@
|
|||||||
|
package server.logic.ws_protocol.JSON.handlers.tempToTest;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_GetUser_Request;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_GetUser_Response;
|
||||||
|
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||||
|
import server.logic.ws_protocol.WireCodes;
|
||||||
|
import shine.db.dao.SolanaUsersDAO;
|
||||||
|
import shine.db.entities.SolanaUserEntry;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
public class Net_GetUser_Handler implements JsonMessageHandler {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(Net_GetUser_Handler.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) {
|
||||||
|
Net_GetUser_Request req = (Net_GetUser_Request) baseRequest;
|
||||||
|
|
||||||
|
if (req.getLogin() == null || req.getLogin().isBlank()) {
|
||||||
|
// тут логичнее BAD_REQUEST, но ты просил: "нет пользователя" тоже 200.
|
||||||
|
// Поэтому BAD_REQUEST оставляем только на реально пустой login.
|
||||||
|
return NetExceptionResponseFactory.error(
|
||||||
|
req,
|
||||||
|
WireCodes.Status.BAD_REQUEST,
|
||||||
|
"BAD_FIELDS",
|
||||||
|
"Некорректные поля: login"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
SolanaUsersDAO usersDAO = SolanaUsersDAO.getInstance();
|
||||||
|
|
||||||
|
try {
|
||||||
|
SolanaUserEntry u = usersDAO.getByLogin(req.getLogin());
|
||||||
|
|
||||||
|
Net_GetUser_Response resp = new Net_GetUser_Response();
|
||||||
|
resp.setOp(req.getOp());
|
||||||
|
resp.setRequestId(req.getRequestId());
|
||||||
|
resp.setStatus(WireCodes.Status.OK);
|
||||||
|
|
||||||
|
if (u == null) {
|
||||||
|
resp.setExists(false);
|
||||||
|
log.info("ℹ️ GetUser: not found for login={}", req.getLogin());
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ВАЖНО:
|
||||||
|
// - Поиск по login был case-insensitive,
|
||||||
|
// - а тут возвращаем login/blockchainName как в БД (с исходным регистром).
|
||||||
|
resp.setExists(true);
|
||||||
|
resp.setLogin(u.getLogin());
|
||||||
|
resp.setBlockchainName(u.getBlockchainName());
|
||||||
|
resp.setSolanaKey(u.getSolanaKey());
|
||||||
|
resp.setBlockchainKey(u.getBlockchainKey());
|
||||||
|
resp.setDeviceKey(u.getDeviceKey());
|
||||||
|
|
||||||
|
log.info("✅ GetUser: found login={}, blockchainName={}", u.getLogin(), u.getBlockchainName());
|
||||||
|
return resp;
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.error("❌ DB error GetUser", e);
|
||||||
|
return NetExceptionResponseFactory.error(
|
||||||
|
req,
|
||||||
|
WireCodes.Status.SERVER_DATA_ERROR,
|
||||||
|
"DB_ERROR",
|
||||||
|
"Ошибка БД"
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ Internal error GetUser", e);
|
||||||
|
return NetExceptionResponseFactory.error(
|
||||||
|
req,
|
||||||
|
WireCodes.Status.INTERNAL_ERROR,
|
||||||
|
"INTERNAL_ERROR",
|
||||||
|
"Внутренняя ошибка сервера"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
// file: server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_AddUser_Response.java
|
||||||
package server.logic.ws_protocol.JSON.handlers.tempToTest.entyties;
|
package server.logic.ws_protocol.JSON.handlers.tempToTest.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
|||||||
@ -0,0 +1,27 @@
|
|||||||
|
package server.logic.ws_protocol.JSON.handlers.tempToTest.entyties;
|
||||||
|
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запрос GetUser — проверка/получение пользователя по login.
|
||||||
|
*
|
||||||
|
* Клиент отправляет:
|
||||||
|
*
|
||||||
|
* {
|
||||||
|
* "op": "GetUser",
|
||||||
|
* "requestId": "u-1",
|
||||||
|
* "payload": {
|
||||||
|
* "login": "AnYa"
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Поиск по login выполняется без учёта регистра.
|
||||||
|
* В ответе возвращаем login/blockchainName с тем регистром, как в БД.
|
||||||
|
*/
|
||||||
|
public class Net_GetUser_Request extends Net_Request {
|
||||||
|
|
||||||
|
private String login;
|
||||||
|
|
||||||
|
public String getLogin() { return login; }
|
||||||
|
public void setLogin(String login) { this.login = login; }
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
package server.logic.ws_protocol.JSON.handlers.tempToTest.entyties;
|
||||||
|
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ответ GetUser.
|
||||||
|
*
|
||||||
|
* Всегда status=200.
|
||||||
|
*
|
||||||
|
* Пример (нет пользователя):
|
||||||
|
* {
|
||||||
|
* "op": "GetUser",
|
||||||
|
* "requestId": "u-1",
|
||||||
|
* "status": 200,
|
||||||
|
* "payload": { "exists": false }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Пример (есть пользователь):
|
||||||
|
* {
|
||||||
|
* "op": "GetUser",
|
||||||
|
* "requestId": "u-1",
|
||||||
|
* "status": 200,
|
||||||
|
* "payload": {
|
||||||
|
* "exists": true,
|
||||||
|
* "login": "Anya",
|
||||||
|
* "blockchainName": "anya-001",
|
||||||
|
* "solanaKey": "...",
|
||||||
|
* "blockchainKey": "...",
|
||||||
|
* "deviceKey": "..."
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
public class Net_GetUser_Response extends Net_Response {
|
||||||
|
|
||||||
|
private Boolean exists;
|
||||||
|
|
||||||
|
private String login;
|
||||||
|
private String blockchainName;
|
||||||
|
private String solanaKey;
|
||||||
|
private String blockchainKey;
|
||||||
|
private String deviceKey;
|
||||||
|
|
||||||
|
public Boolean getExists() { return exists; }
|
||||||
|
public void setExists(Boolean exists) { this.exists = exists; }
|
||||||
|
|
||||||
|
public String getLogin() { return login; }
|
||||||
|
public void setLogin(String login) { this.login = login; }
|
||||||
|
|
||||||
|
public String getBlockchainName() { return blockchainName; }
|
||||||
|
public void setBlockchainName(String blockchainName) { this.blockchainName = blockchainName; }
|
||||||
|
|
||||||
|
public String getSolanaKey() { return solanaKey; }
|
||||||
|
public void setSolanaKey(String solanaKey) { this.solanaKey = solanaKey; }
|
||||||
|
|
||||||
|
public String getBlockchainKey() { return blockchainKey; }
|
||||||
|
public void setBlockchainKey(String blockchainKey) { this.blockchainKey = blockchainKey; }
|
||||||
|
|
||||||
|
public String getDeviceKey() { return deviceKey; }
|
||||||
|
public void setDeviceKey(String deviceKey) { this.deviceKey = deviceKey; }
|
||||||
|
}
|
||||||
@ -13,6 +13,11 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||||||
/**
|
/**
|
||||||
* IT_01_AddUser
|
* IT_01_AddUser
|
||||||
* Создаёт 3 пользователей: TestUser1/2/3 (200 OK или 409 USER_ALREADY_EXISTS).
|
* Создаёт 3 пользователей: TestUser1/2/3 (200 OK или 409 USER_ALREADY_EXISTS).
|
||||||
|
*
|
||||||
|
* Обновление:
|
||||||
|
* - теперь AddUser может вернуть 409 не только USER_ALREADY_EXISTS,
|
||||||
|
* но и BLOCKCHAIN_ALREADY_EXISTS / BLOCKCHAIN_STATE_ALREADY_EXISTS.
|
||||||
|
* - дополнительно проверяем GetUser (status=200 всегда).
|
||||||
*/
|
*/
|
||||||
public class IT_01_AddUser {
|
public class IT_01_AddUser {
|
||||||
|
|
||||||
@ -27,14 +32,32 @@ public class IT_01_AddUser {
|
|||||||
Duration t = Duration.ofSeconds(5);
|
Duration t = Duration.ofSeconds(5);
|
||||||
|
|
||||||
try (WsSession ws = WsSession.open()) {
|
try (WsSession ws = WsSession.open()) {
|
||||||
|
|
||||||
r.ok("AddUser USER1: " + TestConfig.LOGIN());
|
r.ok("AddUser USER1: " + TestConfig.LOGIN());
|
||||||
checkAddUser200or409(r, ws.call("AddUser#USER1", JsonBuilders.addUser(TestConfig.LOGIN()), t));
|
String resp1 = ws.call("AddUser#USER1", JsonBuilders.addUser(TestConfig.LOGIN()), t);
|
||||||
|
checkAddUser200or409(r, resp1);
|
||||||
|
checkGetUserMustExist(r, ws, TestConfig.LOGIN(), t);
|
||||||
|
|
||||||
r.ok("AddUser USER2: " + TestConfig.LOGIN2());
|
r.ok("AddUser USER2: " + TestConfig.LOGIN2());
|
||||||
checkAddUser200or409(r, ws.call("AddUser#USER2", JsonBuilders.addUser(TestConfig.LOGIN2()), t));
|
String resp2 = ws.call("AddUser#USER2", JsonBuilders.addUser(TestConfig.LOGIN2()), t);
|
||||||
|
checkAddUser200or409(r, resp2);
|
||||||
|
checkGetUserMustExist(r, ws, TestConfig.LOGIN2(), t);
|
||||||
|
|
||||||
r.ok("AddUser USER3: " + TestConfig.LOGIN3());
|
r.ok("AddUser USER3: " + TestConfig.LOGIN3());
|
||||||
checkAddUser200or409(r, ws.call("AddUser#USER3", JsonBuilders.addUser(TestConfig.LOGIN3()), t));
|
String resp3 = ws.call("AddUser#USER3", JsonBuilders.addUser(TestConfig.LOGIN3()), t);
|
||||||
|
checkAddUser200or409(r, resp3);
|
||||||
|
checkGetUserMustExist(r, ws, TestConfig.LOGIN3(), t);
|
||||||
|
|
||||||
|
// Доп: проверяем case-insensitive поиск
|
||||||
|
String mixed = mixCase(TestConfig.LOGIN());
|
||||||
|
r.ok("GetUser case-insensitive: запрос=" + mixed + " (должен найти " + TestConfig.LOGIN() + ")");
|
||||||
|
checkGetUserMustExist(r, ws, mixed, t);
|
||||||
|
|
||||||
|
// Доп: проверяем "не существует" (но status=200)
|
||||||
|
String missing = "NoSuchUser_987654321";
|
||||||
|
r.ok("GetUser missing: " + missing);
|
||||||
|
checkGetUserMustNotExist(r, ws, missing, t);
|
||||||
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
r.fail("IT_01_AddUser упал: " + e.getMessage());
|
r.fail("IT_01_AddUser упал: " + e.getMessage());
|
||||||
}
|
}
|
||||||
@ -50,14 +73,138 @@ public class IT_01_AddUser {
|
|||||||
}
|
}
|
||||||
if (st == 409) {
|
if (st == 409) {
|
||||||
String code = JsonParsers.errorCode(resp);
|
String code = JsonParsers.errorCode(resp);
|
||||||
|
|
||||||
|
// раньше был только USER_ALREADY_EXISTS, теперь добавились ещё варианты
|
||||||
if ("USER_ALREADY_EXISTS".equals(code)) {
|
if ("USER_ALREADY_EXISTS".equals(code)) {
|
||||||
r.ok("AddUser: status=409 USER_ALREADY_EXISTS (уже был)");
|
r.ok("AddUser: status=409 USER_ALREADY_EXISTS (уже был)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if ("BLOCKCHAIN_ALREADY_EXISTS".equals(code)) {
|
||||||
|
r.ok("AddUser: status=409 BLOCKCHAIN_ALREADY_EXISTS (blockchainName уже занят)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("BLOCKCHAIN_STATE_ALREADY_EXISTS".equals(code)) {
|
||||||
|
r.ok("AddUser: status=409 BLOCKCHAIN_STATE_ALREADY_EXISTS (blockchain_state уже есть)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
r.fail("AddUser: status=409 но code=" + code + ", resp=" + resp);
|
r.fail("AddUser: status=409 но code=" + code + ", resp=" + resp);
|
||||||
fail("AddUser unexpected 409 code=" + code);
|
fail("AddUser unexpected 409 code=" + code);
|
||||||
}
|
}
|
||||||
r.fail("AddUser: неожиданный status=" + st + ", resp=" + resp);
|
r.fail("AddUser: неожиданный status=" + st + ", resp=" + resp);
|
||||||
fail("AddUser unexpected status=" + st);
|
fail("AddUser unexpected status=" + st);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void checkGetUserMustExist(TestResult r, WsSession ws, String loginQuery, Duration t) {
|
||||||
|
String resp = ws.call("GetUser#" + loginQuery, JsonBuilders.getUser(loginQuery), t);
|
||||||
|
|
||||||
|
int st = JsonParsers.status(resp);
|
||||||
|
if (st != 200) {
|
||||||
|
r.fail("GetUser: ожидали status=200, получили " + st + ", resp=" + resp);
|
||||||
|
fail("GetUser unexpected status=" + st);
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean exists = JsonParsers.exists(resp);
|
||||||
|
if (exists == null || !exists) {
|
||||||
|
r.fail("GetUser: ожидали exists=true, resp=" + resp);
|
||||||
|
fail("GetUser expected exists=true");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, что сервер возвращает данные
|
||||||
|
String login = JsonParsers.userLogin(resp);
|
||||||
|
String blockchainName = JsonParsers.userBlockchainName(resp);
|
||||||
|
String solanaKey = JsonParsers.userSolanaKey(resp);
|
||||||
|
String blockchainKey = JsonParsers.userBlockchainKey(resp);
|
||||||
|
String deviceKey = JsonParsers.userDeviceKey(resp);
|
||||||
|
|
||||||
|
if (isBlank(login) || isBlank(blockchainName) || isBlank(solanaKey) || isBlank(blockchainKey) || isBlank(deviceKey)) {
|
||||||
|
r.fail("GetUser: exists=true, но поля пустые/неполные, resp=" + resp);
|
||||||
|
fail("GetUser returned incomplete user data");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ВАЖНО:
|
||||||
|
// Поиск делается без учета регистра, но login/blockchainName должны вернуться как в БД.
|
||||||
|
// Для тех логинов, которые мы создаем в тесте, это ровно TestConfig.LOGIN*().
|
||||||
|
// Поэтому если запрос был смешанный регистр — сравниваем не с loginQuery, а с "каноничным" логином из конфига.
|
||||||
|
String canonical = canonicalLogin(loginQuery);
|
||||||
|
if (canonical != null) {
|
||||||
|
if (!login.equals(canonical)) {
|
||||||
|
r.fail("GetUser: login должен вернуться как в БД. expected=" + canonical + ", got=" + login + ", resp=" + resp);
|
||||||
|
fail("GetUser wrong login case");
|
||||||
|
}
|
||||||
|
|
||||||
|
String expectedBch = TestConfig.getBlockchainName(canonical);
|
||||||
|
if (!blockchainName.equals(expectedBch)) {
|
||||||
|
r.fail("GetUser: blockchainName должен вернуться как в БД. expected=" + expectedBch + ", got=" + blockchainName + ", resp=" + resp);
|
||||||
|
fail("GetUser wrong blockchainName");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ключи должны совпадать с теми, что AddUser использует при регистрации
|
||||||
|
String expSol = TestConfig.solanaPublicKeyB64(canonical);
|
||||||
|
String expBchKey = TestConfig.blockchainPublicKeyB64(canonical);
|
||||||
|
String expDev = TestConfig.devicePublicKeyB64(canonical);
|
||||||
|
|
||||||
|
if (!solanaKey.equals(expSol)) {
|
||||||
|
r.fail("GetUser: solanaKey mismatch, resp=" + resp);
|
||||||
|
fail("GetUser solanaKey mismatch");
|
||||||
|
}
|
||||||
|
if (!blockchainKey.equals(expBchKey)) {
|
||||||
|
r.fail("GetUser: blockchainKey mismatch, resp=" + resp);
|
||||||
|
fail("GetUser blockchainKey mismatch");
|
||||||
|
}
|
||||||
|
if (!deviceKey.equals(expDev)) {
|
||||||
|
r.fail("GetUser: deviceKey mismatch, resp=" + resp);
|
||||||
|
fail("GetUser deviceKey mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.ok("GetUser: exists=true, login=" + login + ", blockchainName=" + blockchainName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void checkGetUserMustNotExist(TestResult r, WsSession ws, String loginQuery, Duration t) {
|
||||||
|
String resp = ws.call("GetUser#" + loginQuery, JsonBuilders.getUser(loginQuery), t);
|
||||||
|
|
||||||
|
int st = JsonParsers.status(resp);
|
||||||
|
if (st != 200) {
|
||||||
|
r.fail("GetUser(not exist): ожидали status=200, получили " + st + ", resp=" + resp);
|
||||||
|
fail("GetUser(not exist) unexpected status=" + st);
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean exists = JsonParsers.exists(resp);
|
||||||
|
if (exists == null) {
|
||||||
|
r.fail("GetUser(not exist): payload.exists отсутствует, resp=" + resp);
|
||||||
|
fail("GetUser(not exist) missing exists");
|
||||||
|
}
|
||||||
|
if (exists) {
|
||||||
|
r.fail("GetUser(not exist): ожидали exists=false, resp=" + resp);
|
||||||
|
fail("GetUser(not exist) expected exists=false");
|
||||||
|
}
|
||||||
|
|
||||||
|
r.ok("GetUser: exists=false (ok)");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String canonicalLogin(String anyCaseLogin) {
|
||||||
|
if (anyCaseLogin == null) return null;
|
||||||
|
String x = anyCaseLogin.trim();
|
||||||
|
if (x.isEmpty()) return null;
|
||||||
|
|
||||||
|
// Привязка только к нашим тестовым логинам, чтобы не гадать.
|
||||||
|
if (x.equalsIgnoreCase(TestConfig.LOGIN())) return TestConfig.LOGIN();
|
||||||
|
if (x.equalsIgnoreCase(TestConfig.LOGIN2())) return TestConfig.LOGIN2();
|
||||||
|
if (x.equalsIgnoreCase(TestConfig.LOGIN3())) return TestConfig.LOGIN3();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String mixCase(String s) {
|
||||||
|
if (s == null) return null;
|
||||||
|
String x = s.trim();
|
||||||
|
if (x.length() < 2) return x;
|
||||||
|
// простой "микс" без рандома, чтобы тест был детерминированный
|
||||||
|
return Character.toUpperCase(x.charAt(0)) + x.substring(1).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isBlank(String s) {
|
||||||
|
return s == null || s.trim().isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -45,6 +45,21 @@ public final class JsonBuilders {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------- GetUser ----------------
|
||||||
|
|
||||||
|
public static String getUser(String login) {
|
||||||
|
String requestId = TestIds.next("getuser");
|
||||||
|
return """
|
||||||
|
{
|
||||||
|
"op": "GetUser",
|
||||||
|
"requestId": "%s",
|
||||||
|
"payload": {
|
||||||
|
"login": "%s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".formatted(requestId, login);
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------- AuthChallenge ----------------
|
// ---------------- AuthChallenge ----------------
|
||||||
|
|
||||||
public static String authChallenge(String login) {
|
public static String authChallenge(String login) {
|
||||||
|
|||||||
@ -113,4 +113,50 @@ public final class JsonParsers {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------- GetUser helpers ----------------
|
||||||
|
|
||||||
|
public static Boolean exists(String json) {
|
||||||
|
try {
|
||||||
|
JsonNode root = MAPPER.readTree(json);
|
||||||
|
JsonNode payload = root.get("payload");
|
||||||
|
if (payload != null && payload.has("exists")) return payload.get("exists").asBoolean();
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String userLogin(String json) {
|
||||||
|
return getPayloadText(json, "login");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String userBlockchainName(String json) {
|
||||||
|
return getPayloadText(json, "blockchainName");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String userSolanaKey(String json) {
|
||||||
|
return getPayloadText(json, "solanaKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String userBlockchainKey(String json) {
|
||||||
|
return getPayloadText(json, "blockchainKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String userDeviceKey(String json) {
|
||||||
|
return getPayloadText(json, "deviceKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getPayloadText(String json, String field) {
|
||||||
|
try {
|
||||||
|
JsonNode root = MAPPER.readTree(json);
|
||||||
|
JsonNode payload = root.get("payload");
|
||||||
|
if (payload != null && payload.has(field) && !payload.get(field).isNull()) {
|
||||||
|
return payload.get(field).asText();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user