Skip to content

Commit 48d066e

Browse files
committed
add more tests; fix missing translation
1 parent 4545d3a commit 48d066e

4 files changed

Lines changed: 187 additions & 3 deletions

File tree

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
-- Restore empty arrays to empty objects
22
UPDATE image_table SET changes = '{}' WHERE changes = '[]';
33

4-
-- Unwrap single-element arrays back to bare objects
4+
-- Unwrap single-element arrays back to bare objects (skip non-array rows)
55
UPDATE image_table
66
SET changes = changes->0
7-
WHERE jsonb_array_length(changes) = 1;
7+
WHERE jsonb_typeof(changes) = 'array' AND jsonb_array_length(changes) = 1;
88

99
ALTER TABLE image_table ALTER COLUMN changes SET DEFAULT '{}';

editing/blur_test.go

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package editing
2+
3+
import (
4+
"bytes"
5+
"image"
6+
"image/color"
7+
"image/png"
8+
"testing"
9+
)
10+
11+
// makeBlurTestPNG creates a solid-color PNG of the given size.
12+
func makeBlurTestPNG(w, h int, c color.Color) []byte {
13+
img := image.NewRGBA(image.Rect(0, 0, w, h))
14+
for y := range h {
15+
for x := range w {
16+
img.Set(x, y, c)
17+
}
18+
}
19+
var buf bytes.Buffer
20+
if err := png.Encode(&buf, img); err != nil {
21+
panic(err)
22+
}
23+
return buf.Bytes()
24+
}
25+
26+
// --- NewBlurChange validation ---
27+
28+
func TestNewBlurChange_ValidParams(t *testing.T) {
29+
_, err := NewBlurChange(BlurParams{
30+
Region: BlurRegion{X1: 0.1, Y1: 0.1, X2: 0.9, Y2: 0.9},
31+
Radius: 10,
32+
})
33+
if err != nil {
34+
t.Fatalf("expected no error, got %v", err)
35+
}
36+
}
37+
38+
func TestNewBlurChange_CoordOutOfRange(t *testing.T) {
39+
cases := []BlurRegion{
40+
{X1: -0.1, Y1: 0, X2: 0.5, Y2: 0.5},
41+
{X1: 0, Y1: 0, X2: 1.1, Y2: 0.5},
42+
{X1: 0, Y1: -0.1, X2: 0.5, Y2: 0.5},
43+
{X1: 0, Y1: 0, X2: 0.5, Y2: 1.1},
44+
}
45+
for _, r := range cases {
46+
if _, err := NewBlurChange(BlurParams{Region: r, Radius: 5}); err == nil {
47+
t.Errorf("expected error for region %+v", r)
48+
}
49+
}
50+
}
51+
52+
func TestNewBlurChange_ZeroArea(t *testing.T) {
53+
cases := []BlurRegion{
54+
{X1: 0.5, Y1: 0.1, X2: 0.5, Y2: 0.9}, // x1 == x2
55+
{X1: 0.1, Y1: 0.5, X2: 0.9, Y2: 0.5}, // y1 == y2
56+
{X1: 0.8, Y1: 0.1, X2: 0.2, Y2: 0.9}, // x1 > x2
57+
}
58+
for _, r := range cases {
59+
if _, err := NewBlurChange(BlurParams{Region: r, Radius: 5}); err == nil {
60+
t.Errorf("expected error for zero-area region %+v", r)
61+
}
62+
}
63+
}
64+
65+
func TestNewBlurChange_RadiusOutOfRange(t *testing.T) {
66+
validRegion := BlurRegion{X1: 0.1, Y1: 0.1, X2: 0.9, Y2: 0.9}
67+
for _, r := range []int{0, -1, 101} {
68+
if _, err := NewBlurChange(BlurParams{Region: validRegion, Radius: r}); err == nil {
69+
t.Errorf("expected error for radius %d", r)
70+
}
71+
}
72+
}
73+
74+
// --- BlurEditor.Apply ---
75+
76+
func TestBlurApply_PreservesDimensions(t *testing.T) {
77+
base := makeBlurTestPNG(200, 150, color.RGBA{255, 0, 0, 255})
78+
c, _ := NewBlurChange(BlurParams{
79+
Region: BlurRegion{X1: 0.2, Y1: 0.2, X2: 0.8, Y2: 0.8},
80+
Radius: 5,
81+
})
82+
editor := &BlurEditor{}
83+
result, mime, err := editor.Apply(base, c, nil)
84+
if err != nil {
85+
t.Fatal(err)
86+
}
87+
if mime != "image/png" {
88+
t.Fatalf("expected image/png, got %s", mime)
89+
}
90+
img, err := png.Decode(bytes.NewReader(result))
91+
if err != nil {
92+
t.Fatal(err)
93+
}
94+
b := img.Bounds()
95+
if b.Dx() != 200 || b.Dy() != 150 {
96+
t.Fatalf("expected 200x150, got %dx%d", b.Dx(), b.Dy())
97+
}
98+
}
99+
100+
func TestBlurApply_OnlyBlursRegion(t *testing.T) {
101+
// Image: left half red, right half blue.
102+
img := image.NewRGBA(image.Rect(0, 0, 100, 100))
103+
for y := range 100 {
104+
for x := range 100 {
105+
if x < 50 {
106+
img.Set(x, y, color.RGBA{255, 0, 0, 255})
107+
} else {
108+
img.Set(x, y, color.RGBA{0, 0, 255, 255})
109+
}
110+
}
111+
}
112+
var buf bytes.Buffer
113+
png.Encode(&buf, img)
114+
115+
// Blur only the right half.
116+
c, _ := NewBlurChange(BlurParams{
117+
Region: BlurRegion{X1: 0.5, Y1: 0, X2: 1.0, Y2: 1.0},
118+
Radius: 5,
119+
})
120+
editor := &BlurEditor{}
121+
result, _, err := editor.Apply(buf.Bytes(), c, nil)
122+
if err != nil {
123+
t.Fatal(err)
124+
}
125+
out, _ := png.Decode(bytes.NewReader(result))
126+
// Left half pixel should still be pure red.
127+
r, g, b, _ := out.At(10, 50).RGBA()
128+
if r != 0xffff || g != 0 || b != 0 {
129+
t.Fatalf("expected unblurred red pixel at (10,50), got r=%d g=%d b=%d", r>>8, g>>8, b>>8)
130+
}
131+
}
132+
133+
func TestBlurApply_InvalidBase(t *testing.T) {
134+
c, _ := NewBlurChange(BlurParams{
135+
Region: BlurRegion{X1: 0.1, Y1: 0.1, X2: 0.9, Y2: 0.9},
136+
Radius: 5,
137+
})
138+
editor := &BlurEditor{}
139+
if _, _, err := editor.Apply([]byte("not an image"), c, nil); err == nil {
140+
t.Fatal("expected error for invalid base image")
141+
}
142+
}
143+
144+
func TestBlurApply_InvalidJSON(t *testing.T) {
145+
base := makeBlurTestPNG(100, 100, color.White)
146+
editor := &BlurEditor{}
147+
if _, _, err := editor.Apply(base, Change{Type: "blur", Params: []byte("not json")}, nil); err == nil {
148+
t.Fatal("expected error for invalid params JSON")
149+
}
150+
}
151+
152+
func TestBlurApply_RadiusIndependence(t *testing.T) {
153+
// Both radius=1 and radius=50 should complete without error on a real image.
154+
base := makeBlurTestPNG(300, 300, color.RGBA{128, 64, 200, 255})
155+
editor := &BlurEditor{}
156+
for _, radius := range []int{1, 50, 100} {
157+
c, _ := NewBlurChange(BlurParams{
158+
Region: BlurRegion{X1: 0.1, Y1: 0.1, X2: 0.9, Y2: 0.9},
159+
Radius: radius,
160+
})
161+
result, _, err := editor.Apply(base, c, nil)
162+
if err != nil {
163+
t.Fatalf("radius %d: unexpected error: %v", radius, err)
164+
}
165+
if len(result) == 0 {
166+
t.Fatalf("radius %d: empty result", radius)
167+
}
168+
}
169+
}

editing/changeset.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ type ApplyResult struct {
3232
// ApplyChangeSet applies each Change in the ChangeSet in order, piping
3333
// output bytes into the next step, then serializes the full ChangeSet.
3434
func ApplyChangeSet(cs ChangeSet, baseImageId string, fetchImage FetchImageFunc) (*ApplyResult, error) {
35+
if len(cs) == 0 {
36+
return nil, fmt.Errorf("change set must contain at least one change")
37+
}
38+
3539
currentBytes, err := fetchImage(baseImageId)
3640
if err != nil {
3741
return nil, fmt.Errorf("failed to fetch base image: %w", err)

web_client/src/localization/th.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,18 @@
166166
"derivedFrom": "ต้นฉบับ: {{name}}",
167167
"watermarkApplied": "ใส่ลายน้ำสำเร็จ",
168168
"watermarkFailed": "ใส่ลายน้ำไม่สำเร็จ",
169-
"watermark": "ลายน้ำ"
169+
"watermark": "ลายน้ำ",
170+
"blur": "เบลอ",
171+
"blurApplied": "ใช้เอฟเฟกต์เบลอสำเร็จ",
172+
"blurFailed": "ใช้เอฟเฟกต์เบลอไม่สำเร็จ"
173+
},
174+
"blurTool": {
175+
"hint": "ลากบนภาพเพื่อเลือกพื้นที่ที่ต้องการเบลอ",
176+
"noRegion": "ยังไม่ได้เลือกพื้นที่",
177+
"regionSelected": "({{x1}}%, {{y1}}%) → ({{x2}}%, {{y2}}%)",
178+
"radius": "ความแรงของเบลอ: {{value}}",
179+
"applying": "กำลังใช้งาน...",
180+
"applyBlur": "ใช้เอฟเฟกต์เบลอ"
170181
},
171182
"watermarkTool": {
172183
"overlayImage": "ภาพซ้อนทับ",

0 commit comments

Comments
 (0)