Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitcf5fc5b

Browse files
authored
Merge pull request#470 from tcheeric/test/critical-nip-tests-implementation
Pull Request: Add Critical NIP Tests + Phase 3 & 4 Documentation
2 parentscfac008 +d191718 commitcf5fc5b

File tree

26 files changed

+4138
-96
lines changed

26 files changed

+4138
-96
lines changed
Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
#Exception Message Standards
2+
3+
**Created:** 2025-10-07
4+
**Purpose:** Standardize exception messages across nostr-java for better debugging and user experience
5+
6+
---
7+
8+
##Guiding Principles
9+
10+
1.**Be specific** - Include what failed and why
11+
2.**Include context** - Add relevant values (IDs, names, types)
12+
3.**Use consistent format** - Follow established patterns
13+
4.**Be actionable** - Help developers understand how to fix the issue
14+
15+
---
16+
17+
##Standard Message Formats
18+
19+
###Pattern 1: "Failed to {action}: {reason}"
20+
21+
**Use for:** Operational failures (encoding, decoding, network, I/O)
22+
23+
**Examples:**
24+
```java
25+
// ✅ Good
26+
thrownewEventEncodingException("Failed to encode event message: invalid JSON structure");
27+
thrownewNostrNetworkException("Failed to connect to relay: connection timeout after 60s");
28+
thrownewNostrCryptoException("Failed to sign event [id="+ eventId+"]: private key is null");
29+
30+
// ❌ Bad
31+
thrownewRuntimeException(e);// No context!
32+
thrownewEventEncodingException("Error");// Too vague!
33+
```
34+
35+
###Pattern 2: "Invalid {entity}: {reason}"
36+
37+
**Use for:** Validation failures
38+
39+
**Examples:**
40+
```java
41+
// ✅ Good
42+
thrownewIllegalArgumentException("Invalid event kind: must be between 0 and 65535, got"+ kind);
43+
thrownewNostrProtocolException("Invalid event: missing required field 'content'");
44+
thrownewIllegalArgumentException("Invalid tag type: expected EventTag, got"+ tag.getClass().getSimpleName());
45+
46+
// ❌ Bad
47+
thrownewIllegalArgumentException("The event is not a channel creation event");// Use "Invalid" prefix
48+
thrownewIllegalArgumentException("tag must be of type RelaysTag");// Use "Invalid" prefix
49+
```
50+
51+
###Pattern 3: "Cannot {action}: {reason}"
52+
53+
**Use for:** Prevented operations (state issues)
54+
55+
**Examples:**
56+
```java
57+
// ✅ Good
58+
thrownewIllegalStateException("Cannot sign event: sender identity is required");
59+
thrownewIllegalStateException("Cannot verify event: event is not signed");
60+
thrownewNostrProtocolException("Cannot create zap request: relays tag or relay list is required");
61+
62+
// ❌ Bad
63+
thrownewIllegalStateException("Sender identity is required for zap operations");// Use "Cannot" prefix
64+
thrownewIllegalStateException("The event is not signed");// Use "Cannot" prefix
65+
```
66+
67+
###Pattern 4: "{Entity} is/are {state}"
68+
69+
**Use for:** Simple state assertions
70+
71+
**Examples:**
72+
```java
73+
// ✅ Good - for simple cases
74+
thrownewNoSuchElementException("No matching p-tag found in event tags");
75+
thrownewIllegalArgumentException("Relay URL is null or empty");
76+
77+
// ✅ Also good - with more context
78+
thrownewNoSuchElementException("No matching p-tag found in event [id="+ eventId+"]");
79+
```
80+
81+
---
82+
83+
##Exception Type Selection
84+
85+
###Use Domain Exceptions First
86+
87+
Prefer nostr-java domain exceptions over generic Java exceptions:
88+
89+
```java
90+
// ✅ Preferred - Domain exceptions
91+
thrownewNostrProtocolException("Invalid event: ...");
92+
thrownewNostrCryptoException("Failed to sign: ...");
93+
thrownewNostrEncodingException("Failed to decode: ...");
94+
thrownewNostrNetworkException("Failed to connect: ...");
95+
96+
// ⚠️ Acceptable - Standard Java exceptions when appropriate
97+
thrownewIllegalArgumentException("Invalid parameter: ...");
98+
thrownewIllegalStateException("Cannot perform action: ...");
99+
thrownewNoSuchElementException("Element not found: ...");
100+
101+
// ❌ Avoid - Bare RuntimeException
102+
thrownewRuntimeException(e);// Use specific exception type!
103+
thrownewRuntimeException("Something failed");// Use NostrProtocolException or other domain exception
104+
```
105+
106+
###Domain Exception Usage Guide
107+
108+
| Exception Type| When to Use| Example|
109+
|----------------|-------------|---------|
110+
|`NostrProtocolException`| NIP violations, invalid events/messages| Invalid event structure, missing required tags|
111+
|`NostrCryptoException`| Signing, verification, encryption failures| Failed to sign event, invalid signature|
112+
|`NostrEncodingException`| JSON, Bech32, hex encoding/decoding errors| Invalid JSON, malformed Bech32|
113+
|`NostrNetworkException`| Relay communication, timeouts, connection errors| Connection timeout, relay rejected event|
114+
|`IllegalArgumentException`| Invalid method parameters| Null parameter, out of range value|
115+
|`IllegalStateException`| Object state prevents operation| Event not signed, identity not set|
116+
|`NoSuchElementException`| Expected element not found| Tag not found, subscription not found|
117+
118+
---
119+
120+
##Context Inclusion
121+
122+
###Include Relevant Context Values
123+
124+
**Good examples:**
125+
```java
126+
// Event ID
127+
thrownewNostrCryptoException("Failed to sign event [id="+ event.getId()+"]: private key is null");
128+
129+
// Kind value
130+
thrownewIllegalArgumentException("Invalid event kind ["+ kind+"]: must be between 0 and 65535");
131+
132+
// Tag type
133+
thrownewIllegalArgumentException("Invalid tag type: expected"+ expectedType+", got"+ actualType);
134+
135+
// Relay URL
136+
thrownewNostrNetworkException("Failed to connect to relay ["+ relay.getUrl()+"]:"+ cause);
137+
138+
// Field name
139+
thrownewNostrProtocolException("Invalid event: missing required field '"+ fieldName+"'");
140+
```
141+
142+
###Use String.format() for Complex Messages
143+
144+
```java
145+
// ✅ Good - Readable with String.format
146+
thrownewNostrCryptoException(
147+
String.format("Failed to sign event [id=%s, kind=%d]: %s",
148+
event.getId(), event.getKind(), reason)
149+
);
150+
151+
// ⚠️ Okay - String concatenation for simple messages
152+
thrownewIllegalArgumentException("Invalid kind:"+ kind);
153+
```
154+
155+
---
156+
157+
##Cause Chain Preservation
158+
159+
**Always preserve the original exception as the cause:**
160+
161+
```java
162+
// ✅ Good - Preserve cause
163+
try {
164+
mapper.writeValueAsString(event);
165+
}catch (JsonProcessingException e) {
166+
thrownewEventEncodingException("Failed to encode event message", e);
167+
}
168+
169+
// ❌ Bad - Lost stack trace
170+
try {
171+
mapper.writeValueAsString(event);
172+
}catch (JsonProcessingException e) {
173+
thrownewEventEncodingException("Failed to encode event message");// Cause lost!
174+
}
175+
176+
// ❌ Bad - No context
177+
try {
178+
mapper.writeValueAsString(event);
179+
}catch (JsonProcessingException e) {
180+
thrownewRuntimeException(e);// What operation failed?
181+
}
182+
```
183+
184+
---
185+
186+
##Common Patterns by Module
187+
188+
###Event Validation (nostr-java-event)
189+
190+
```java
191+
// Required field validation
192+
if (content==null) {
193+
thrownewNostrProtocolException("Invalid event: missing required field 'content'");
194+
}
195+
196+
// Kind range validation
197+
if (kind<0|| kind>65535) {
198+
thrownewIllegalArgumentException("Invalid event kind ["+ kind+"]: must be between 0 and 65535");
199+
}
200+
201+
// Signature validation
202+
if (!event.verify()) {
203+
thrownewNostrCryptoException("Failed to verify event [id="+ event.getId()+"]: invalid signature");
204+
}
205+
```
206+
207+
###Encoding/Decoding (nostr-java-event)
208+
209+
```java
210+
// JSON encoding
211+
try {
212+
return mapper.writeValueAsString(message);
213+
}catch (JsonProcessingException e) {
214+
thrownewEventEncodingException("Failed to encode"+ messageType+" message", e);
215+
}
216+
217+
// Bech32 decoding
218+
try {
219+
returnBech32.decode(bech32String);
220+
}catch (Exception e) {
221+
thrownewNostrEncodingException("Failed to decode Bech32 string ["+ bech32String+"]", e);
222+
}
223+
```
224+
225+
###API Operations (nostr-java-api)
226+
227+
```java
228+
// State preconditions
229+
if (sender==null) {
230+
thrownewIllegalStateException("Cannot create event: sender identity is required");
231+
}
232+
233+
// Type checking
234+
if (!(taginstanceofRelaysTag)) {
235+
thrownewIllegalArgumentException(
236+
"Invalid tag type: expected RelaysTag, got"+ tag.getClass().getSimpleName()
237+
);
238+
}
239+
240+
// Event type validation
241+
if (event.getKind()!=Kind.CHANNEL_CREATE.getValue()) {
242+
thrownewIllegalArgumentException(
243+
"Invalid event: expected kind"+Kind.CHANNEL_CREATE+", got"+ event.getKind()
244+
);
245+
}
246+
```
247+
248+
---
249+
250+
##Migration Examples
251+
252+
###Before → After Examples
253+
254+
####Example 1: Generic RuntimeException
255+
```java
256+
// ❌ Before
257+
thrownewRuntimeException(e);
258+
259+
// ✅ After
260+
thrownewNostrEncodingException("Failed to serialize event", e);
261+
```
262+
263+
####Example 2: Vague Message
264+
```java
265+
// ❌ Before
266+
thrownewIllegalArgumentException("The event is not a channel creation event");
267+
268+
// ✅ After
269+
thrownewIllegalArgumentException(
270+
"Invalid event: expected kind"+Kind.CHANNEL_CREATE+", got"+ event.getKind()
271+
);
272+
```
273+
274+
####Example 3: Missing Context
275+
```java
276+
// ❌ Before
277+
thrownewIllegalStateException("Sender identity is required for zap operations");
278+
279+
// ✅ After
280+
thrownewIllegalStateException("Cannot create zap request: sender identity is required");
281+
```
282+
283+
####Example 4: No Cause Preservation
284+
```java
285+
// ❌ Before
286+
try {
287+
algorithm.sign(data);
288+
}catch (Exception e) {
289+
thrownewRuntimeException("Signing failed");
290+
}
291+
292+
// ✅ After
293+
try {
294+
algorithm.sign(data);
295+
}catch (Exception e) {
296+
thrownewNostrCryptoException("Failed to sign event [id="+ eventId+"]:"+ e.getMessage(), e);
297+
}
298+
```
299+
300+
---
301+
302+
##Audit Checklist
303+
304+
When reviewing exception throws:
305+
306+
-[ ]**Type:** Is a domain exception used? (NostrProtocolException, NostrCryptoException, etc.)
307+
-[ ]**Format:** Does it follow a standard pattern? (Failed to.., Invalid.., Cannot..)
308+
-[ ]**Context:** Are relevant values included? (IDs, kinds, types, URLs)
309+
-[ ]**Cause:** Is the original exception preserved in the chain?
310+
-[ ]**Actionable:** Can a developer understand what went wrong and how to fix it?
311+
312+
---
313+
314+
##Statistics
315+
316+
**Current Status (as of 2025-10-07):**
317+
- Total exception throws: 209
318+
- Following standard patterns:~85% (estimated)
319+
- Need improvement:~15% (bare RuntimeException, vague messages, missing context)
320+
321+
**Priority Areas for Improvement:**
322+
1. Replace bare`throw new RuntimeException(e)` with domain exceptions
323+
2. Standardize validation messages to use "Invalid {entity}: {reason}" format
324+
3. Add "Cannot {action}" prefix to IllegalStateException messages
325+
4. Include context values (event IDs, kinds, types) where missing
326+
327+
---
328+
329+
**Last Updated:** 2025-10-07
330+
**Status:** Standards defined, gradual adoption recommended
331+
**Enforcement:** Code review + IDE inspections

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp