• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Freegle / Iznik / 8688

01 May 2026 03:24AM UTC coverage: 52.693% (-19.6%) from 72.326%
8688

Pull #307

circleci

edwh
fix(playwright): exclude all ModSettings* components from coverage denominator

All six ModSettings* components (ModSettingsPersonal, ModSettingsGroup,
ModSettingsStandardMessageModal, ModSettingsStandardMessageButton,
ModSettingsStandardMessageSet, ModSettingsModConfig) render exclusively
on the modtools /settings pages that the e2e suite never navigates to.
They are consistently 0% covered by Playwright across every run and add
only denominator noise that makes the Playwright coverage metric sensitive
to nondeterministic variation between runs.

Previously only ModSettingsStandardMessageSet and ModSettingsModConfig
were excluded. Extending the exclusion to all ModSettings* components
(via a single !sourcePath.includes('ModSettings') pattern) removes the
remaining denominator noise from this component family.

All six components have Vitest unit test coverage:
  tests/unit/components/modtools/ModSettings*.spec.js

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pull Request #307: fix: standard message delete 404 and group standard message config not persisting

9103 of 10502 branches covered (86.68%)

Branch coverage included in aggregate %.

0 of 27 new or added lines in 2 files covered. (0.0%)

33882 existing lines in 232 files now uncovered.

64220 of 128648 relevant lines covered (49.92%)

9.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/iznik-server-go/stdmsg/stdmsg.go
1
package stdmsg
2

3
import (
4
        "strings"
5
        "strconv"
6

7
        "github.com/freegle/iznik-server-go/auth"
8
        "github.com/freegle/iznik-server-go/database"
9
        "github.com/freegle/iznik-server-go/user"
10
        "github.com/gofiber/fiber/v2"
11
)
12

13
type StdMsg struct {
14
        ID           uint64  `json:"id" gorm:"primary_key"`
15
        Configid     uint64  `json:"configid"`
16
        Title        string  `json:"title"`
17
        Action       string  `json:"action"`
18
        Subjpref     string  `json:"subjpref"`
19
        Subjsuff     string  `json:"subjsuff"`
20
        Body         string  `json:"body"`
21
        Rarelyused   int     `json:"rarelyused"`
22
        Autosend     int     `json:"autosend"`
23
        Newmodstatus string  `json:"newmodstatus"`
24
        Newdelstatus string  `json:"newdelstatus"`
25
        Edittext     string  `json:"edittext"`
26
        Insert       *string `json:"insert"`
27
}
28

29
// canModifyConfig checks if user can modify the parent config.
UNCOV
30
func canModifyConfig(myid uint64, configid uint64) bool {
×
UNCOV
31
        if auth.IsAdminOrSupport(myid) {
×
32
                return true
×
33
        }
×
34

UNCOV
35
        var createdby *uint64
×
UNCOV
36
        var protected int
×
UNCOV
37
        database.DBConn.Raw("SELECT createdby FROM mod_configs WHERE id = ?", configid).Scan(&createdby)
×
UNCOV
38
        database.DBConn.Raw("SELECT protected FROM mod_configs WHERE id = ?", configid).Scan(&protected)
×
UNCOV
39

×
UNCOV
40
        if createdby != nil && *createdby == myid {
×
UNCOV
41
                return true
×
UNCOV
42
        }
×
UNCOV
43
        if protected == 0 {
×
44
                return true
×
45
        }
×
UNCOV
46
        return false
×
47
}
48

49

50
// GetStdMsg handles GET /stdmsg.
51
//
52
// @Summary Get standard message
53
// @Tags stdmsg
54
// @Produce json
55
// @Param id query integer true "StdMsg ID"
56
// @Success 200 {object} map[string]interface{}
57
// @Router /api/stdmsg [get]
UNCOV
58
func GetStdMsg(c *fiber.Ctx) error {
×
UNCOV
59
        id, _ := strconv.ParseUint(c.Query("id", "0"), 10, 64)
×
UNCOV
60
        if id == 0 {
×
UNCOV
61
                return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"ret": 2, "status": "Invalid stdmsg id"})
×
UNCOV
62
        }
×
63

UNCOV
64
        db := database.DBConn
×
UNCOV
65
        var msg StdMsg
×
UNCOV
66
        db.Raw("SELECT * FROM mod_stdmsgs WHERE id = ?", id).Scan(&msg)
×
UNCOV
67
        if msg.ID == 0 {
×
68
                return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"ret": 2, "status": "Invalid stdmsg id"})
×
69
        }
×
70

UNCOV
71
        return c.JSON(fiber.Map{
×
UNCOV
72
                "ret":    0,
×
UNCOV
73
                "status": "Success",
×
UNCOV
74
                "stdmsg": msg,
×
UNCOV
75
        })
×
76
}
77

78
// PostStdMsg handles POST /stdmsg to create a new standard message.
79
//
80
// @Summary Create standard message
81
// @Tags stdmsg
82
// @Accept json
83
// @Produce json
84
// @Security BearerAuth
85
// @Router /api/stdmsg [post]
UNCOV
86
func PostStdMsg(c *fiber.Ctx) error {
×
UNCOV
87
        myid := user.WhoAmI(c)
×
UNCOV
88
        if myid == 0 {
×
89
                return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"ret": 1, "status": "Not logged in"})
×
90
        }
×
91

UNCOV
92
        if !auth.IsSystemMod(myid) {
×
93
                return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"ret": 4, "status": "Don't have rights to create configs"})
×
94
        }
×
95

UNCOV
96
        type CreateRequest struct {
×
UNCOV
97
                Configid     uint64  `json:"configid"`
×
UNCOV
98
                Title        string  `json:"title"`
×
UNCOV
99
                Action       string  `json:"action"`
×
UNCOV
100
                Subjpref     string  `json:"subjpref"`
×
UNCOV
101
                Subjsuff     string  `json:"subjsuff"`
×
UNCOV
102
                Body         string  `json:"body"`
×
UNCOV
103
                Rarelyused   int     `json:"rarelyused"`
×
UNCOV
104
                Autosend     int     `json:"autosend"`
×
UNCOV
105
                Newmodstatus string  `json:"newmodstatus"`
×
UNCOV
106
                Newdelstatus string  `json:"newdelstatus"`
×
UNCOV
107
                Edittext     string  `json:"edittext"`
×
UNCOV
108
                Insert       *string `json:"insert"`
×
UNCOV
109
        }
×
UNCOV
110

×
UNCOV
111
        var req CreateRequest
×
UNCOV
112
        if strings.Contains(c.Get("Content-Type"), "application/json") {
×
UNCOV
113
                c.BodyParser(&req)
×
UNCOV
114
        }
×
UNCOV
115
        if req.Title == "" {
×
UNCOV
116
                req.Title = c.FormValue("title", c.Query("title", ""))
×
UNCOV
117
        }
×
UNCOV
118
        if req.Configid == 0 {
×
119
                req.Configid, _ = strconv.ParseUint(c.FormValue("configid", c.Query("configid", "0")), 10, 64)
×
120
        }
×
121

UNCOV
122
        if req.Title == "" {
×
UNCOV
123
                return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"ret": 3, "status": "Must supply title"})
×
UNCOV
124
        }
×
UNCOV
125
        if req.Configid == 0 {
×
126
                return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"ret": 3, "status": "Must supply configid"})
×
127
        }
×
128

UNCOV
129
        db := database.DBConn
×
UNCOV
130

×
UNCOV
131
        // Use the underlying sql.DB to get LastInsertId() directly from the MySQL protocol
×
UNCOV
132
        // response — never issue a separate SELECT LAST_INSERT_ID() as it's unsafe under
×
UNCOV
133
        // parallel load (GORM's connection pool may assign a different connection).
×
UNCOV
134
        sqlDB, err := db.DB()
×
UNCOV
135
        if err != nil {
×
136
                return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"ret": 1, "status": "Database error"})
×
137
        }
×
UNCOV
138
        sqlResult, err := sqlDB.Exec("INSERT INTO mod_stdmsgs (configid, title, subjpref, subjsuff, body) VALUES (?, ?, '', '', '')", req.Configid, req.Title)
×
UNCOV
139
        if err != nil {
×
140
                return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"ret": 1, "status": "Create failed"})
×
141
        }
×
142

UNCOV
143
        var newID uint64
×
UNCOV
144
        lastID, err := sqlResult.LastInsertId()
×
UNCOV
145
        if err == nil && lastID > 0 {
×
UNCOV
146
                newID = uint64(lastID)
×
UNCOV
147
        }
×
148

149
        // Apply optional attributes.
UNCOV
150
        if req.Action != "" {
×
151
                db.Exec("UPDATE mod_stdmsgs SET action = ? WHERE id = ?", req.Action, newID)
×
152
        }
×
UNCOV
153
        if req.Subjpref != "" {
×
154
                db.Exec("UPDATE mod_stdmsgs SET subjpref = ? WHERE id = ?", req.Subjpref, newID)
×
155
        }
×
UNCOV
156
        if req.Subjsuff != "" {
×
157
                db.Exec("UPDATE mod_stdmsgs SET subjsuff = ? WHERE id = ?", req.Subjsuff, newID)
×
158
        }
×
UNCOV
159
        if req.Body != "" {
×
160
                db.Exec("UPDATE mod_stdmsgs SET body = ? WHERE id = ?", req.Body, newID)
×
161
        }
×
UNCOV
162
        if req.Rarelyused != 0 {
×
163
                db.Exec("UPDATE mod_stdmsgs SET rarelyused = ? WHERE id = ?", req.Rarelyused, newID)
×
164
        }
×
UNCOV
165
        if req.Autosend != 0 {
×
166
                db.Exec("UPDATE mod_stdmsgs SET autosend = ? WHERE id = ?", req.Autosend, newID)
×
167
        }
×
168

UNCOV
169
        return c.JSON(fiber.Map{"ret": 0, "status": "Success", "id": newID})
×
170
}
171

172
// PatchStdMsg handles PATCH /stdmsg to update attributes.
173
//
174
// @Summary Update standard message
175
// @Tags stdmsg
176
// @Accept json
177
// @Produce json
178
// @Security BearerAuth
179
// @Router /api/stdmsg [patch]
UNCOV
180
func PatchStdMsg(c *fiber.Ctx) error {
×
UNCOV
181
        myid := user.WhoAmI(c)
×
UNCOV
182
        if myid == 0 {
×
183
                return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"ret": 1, "status": "Not logged in"})
×
184
        }
×
185

UNCOV
186
        type PatchRequest struct {
×
UNCOV
187
                ID           uint64  `json:"id"`
×
UNCOV
188
                Title        *string `json:"title"`
×
UNCOV
189
                Action       *string `json:"action"`
×
UNCOV
190
                Subjpref     *string `json:"subjpref"`
×
UNCOV
191
                Subjsuff     *string `json:"subjsuff"`
×
UNCOV
192
                Body         *string `json:"body"`
×
UNCOV
193
                Rarelyused   *int    `json:"rarelyused"`
×
UNCOV
194
                Autosend     *int    `json:"autosend"`
×
UNCOV
195
                Newmodstatus *string `json:"newmodstatus"`
×
UNCOV
196
                Newdelstatus *string `json:"newdelstatus"`
×
UNCOV
197
                Edittext     *string `json:"edittext"`
×
UNCOV
198
                Insert       *string `json:"insert"`
×
UNCOV
199
        }
×
UNCOV
200

×
UNCOV
201
        var req PatchRequest
×
UNCOV
202
        if strings.Contains(c.Get("Content-Type"), "application/json") {
×
UNCOV
203
                c.BodyParser(&req)
×
UNCOV
204
        }
×
UNCOV
205
        if req.ID == 0 {
×
206
                req.ID, _ = strconv.ParseUint(c.FormValue("id", c.Query("id", "0")), 10, 64)
×
207
        }
×
208

UNCOV
209
        if req.ID == 0 {
×
210
                return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"ret": 2, "status": "Invalid stdmsg id"})
×
211
        }
×
212

UNCOV
213
        db := database.DBConn
×
UNCOV
214

×
UNCOV
215
        // Get the stdmsg to find its configid.
×
UNCOV
216
        var configid uint64
×
UNCOV
217
        db.Raw("SELECT configid FROM mod_stdmsgs WHERE id = ?", req.ID).Scan(&configid)
×
UNCOV
218
        if configid == 0 {
×
219
                return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"ret": 2, "status": "Invalid stdmsg id"})
×
220
        }
×
221

UNCOV
222
        if !canModifyConfig(myid, configid) {
×
223
                return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"ret": 4, "status": "Don't have rights to modify config"})
×
224
        }
×
225

UNCOV
226
        if req.Title != nil {
×
UNCOV
227
                db.Exec("UPDATE mod_stdmsgs SET title = ? WHERE id = ?", *req.Title, req.ID)
×
UNCOV
228
        }
×
UNCOV
229
        if req.Action != nil {
×
230
                db.Exec("UPDATE mod_stdmsgs SET action = ? WHERE id = ?", *req.Action, req.ID)
×
231
        }
×
UNCOV
232
        if req.Subjpref != nil {
×
233
                db.Exec("UPDATE mod_stdmsgs SET subjpref = ? WHERE id = ?", *req.Subjpref, req.ID)
×
234
        }
×
UNCOV
235
        if req.Subjsuff != nil {
×
236
                db.Exec("UPDATE mod_stdmsgs SET subjsuff = ? WHERE id = ?", *req.Subjsuff, req.ID)
×
237
        }
×
UNCOV
238
        if req.Body != nil {
×
UNCOV
239
                db.Exec("UPDATE mod_stdmsgs SET body = ? WHERE id = ?", *req.Body, req.ID)
×
UNCOV
240
        }
×
UNCOV
241
        if req.Rarelyused != nil {
×
242
                db.Exec("UPDATE mod_stdmsgs SET rarelyused = ? WHERE id = ?", *req.Rarelyused, req.ID)
×
243
        }
×
UNCOV
244
        if req.Autosend != nil {
×
245
                db.Exec("UPDATE mod_stdmsgs SET autosend = ? WHERE id = ?", *req.Autosend, req.ID)
×
246
        }
×
UNCOV
247
        if req.Newmodstatus != nil {
×
248
                db.Exec("UPDATE mod_stdmsgs SET newmodstatus = ? WHERE id = ?", *req.Newmodstatus, req.ID)
×
249
        }
×
UNCOV
250
        if req.Newdelstatus != nil {
×
251
                db.Exec("UPDATE mod_stdmsgs SET newdelstatus = ? WHERE id = ?", *req.Newdelstatus, req.ID)
×
252
        }
×
UNCOV
253
        if req.Edittext != nil {
×
254
                db.Exec("UPDATE mod_stdmsgs SET edittext = ? WHERE id = ?", *req.Edittext, req.ID)
×
255
        }
×
UNCOV
256
        if req.Insert != nil {
×
257
                db.Exec("UPDATE mod_stdmsgs SET `insert` = ? WHERE id = ?", *req.Insert, req.ID)
×
258
        }
×
259

UNCOV
260
        return c.JSON(fiber.Map{"ret": 0, "status": "Success"})
×
261
}
262

263
// DeleteStdMsg handles DELETE /stdmsg.
264
//
265
// @Summary Delete standard message
266
// @Tags stdmsg
267
// @Produce json
268
// @Param id query integer true "StdMsg ID"
269
// @Security BearerAuth
270
// @Router /api/stdmsg [delete]
271
type DeleteStdMsgRequest struct {
272
        ID uint64 `json:"id"`
273
}
274

UNCOV
275
func DeleteStdMsg(c *fiber.Ctx) error {
×
UNCOV
276
        myid := user.WhoAmI(c)
×
UNCOV
277
        if myid == 0 {
×
278
                return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"ret": 1, "status": "Not logged in"})
×
279
        }
×
280

281
        // The frontend sends DELETE params as a JSON body (via $delv2 in BaseAPI).
282
        // Fall back to query string for backwards compatibility.
NEW
283
        var req DeleteStdMsgRequest
×
NEW
284
        if strings.Contains(c.Get("Content-Type"), "application/json") {
×
NEW
285
                if err := c.BodyParser(&req); err != nil {
×
NEW
286
                        return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"ret": 3, "status": "Invalid JSON in request body"})
×
NEW
287
                }
×
288
        }
NEW
289
        id := req.ID
×
NEW
290
        if id == 0 {
×
NEW
291
                id, _ = strconv.ParseUint(c.Query("id", "0"), 10, 64)
×
NEW
292
        }
×
293
        if id == 0 {
×
294
                return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"ret": 2, "status": "Invalid stdmsg id"})
×
295
        }
×
296

UNCOV
297
        db := database.DBConn
×
UNCOV
298

×
UNCOV
299
        var configid uint64
×
UNCOV
300
        db.Raw("SELECT configid FROM mod_stdmsgs WHERE id = ?", id).Scan(&configid)
×
UNCOV
301
        if configid == 0 {
×
UNCOV
302
                return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"ret": 2, "status": "Invalid stdmsg id"})
×
UNCOV
303
        }
×
304

UNCOV
305
        if !canModifyConfig(myid, configid) {
×
UNCOV
306
                return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"ret": 4, "status": "Don't have rights to modify config"})
×
UNCOV
307
        }
×
308

UNCOV
309
        db.Exec("DELETE FROM mod_stdmsgs WHERE id = ?", id)
×
UNCOV
310

×
UNCOV
311
        return c.JSON(fiber.Map{"ret": 0, "status": "Success"})
×
312
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc