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

statuscompliance / status-backend / 15538225882

09 Jun 2025 03:27PM UTC coverage: 67.202% (+1.9%) from 65.28%
15538225882

push

github

alvarobernal2412
test: refactor computation routes test

624 of 1004 branches covered (62.15%)

Branch coverage included in aggregate %.

6 of 20 new or added lines in 1 file covered. (30.0%)

13 existing lines in 1 file now uncovered.

1384 of 1984 relevant lines covered (69.76%)

13.53 hits per line

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

60.78
/src/controllers/computation.controller.js
1
import { models } from '../models/models.js';
2
import { Op , Sequelize} from 'sequelize';
3
import { checkRequiredProperties } from '../utils/checkRequiredProperties.js';
4
import nodered from '../config/nodered.js';
5
import { v4 as uuidv4 } from 'uuid';
6
import redis from '../config/redis.js';
7
import { calculateCompliance } from '../utils/calculateCompliance.js';
8
import { handleControllerError } from '../utils/errorHandler.js';
9

10
const API_PREFIX = process.env.API_PREFIX;
34✔
11

12
export async function getComputations(req, res) {
13
  try {
2✔
14
    const computations = await models.Computation.findAll();
2✔
15
    res.status(200).json(computations);
1✔
16
  } catch (error) {
17
    handleControllerError(res, error, 'Failed to get computations');
1✔
18
  }
19
}
20

21
export async function getComputationsById(req, res) {
22
  try {
4✔
23
    const { id } = req.params;
4✔
24
    const computations = await models.Computation.findAll({
4✔
25
      where: { computationGroup: id },
26
    });
27
    const ready = await redis.get(id);
3✔
28
    if (computations.length === 0) {
3✔
29
      return res.status(404).json({ message: 'Computations not found' });
1✔
30
    }
31
    if (ready !== 'true') {
2✔
32
      return res.status(202).json({ message: 'Not ready yet' });
1✔
33
    }
34
    return res.status(200).json({
1✔
35
      code: 200,
36
      message: 'OK',
37
      computations: calculateCompliance(computations),
38
    });
39
  } catch (error) {
40
    handleControllerError(res, error, 'Failed to get computation by ID');
1✔
41
  }
42
}
43

44
export async function getComputationsByControlId(req, res) {
UNCOV
45
  try {
×
UNCOV
46
    const { controlId } = req.params;
×
UNCOV
47
    const computations = await models.Computation.findAll({
×
48
      where: { controlId },
49
    });
50
    res.status(200).json(computations);
×
51
  } catch (error) {
UNCOV
52
    res.status(500).json({
×
53
      message: `Failed to get computations, error: ${error.message}`,
54
    });
55
  }
56
}
57

58
export async function getComputationsByControlIdAndCreationDate(req, res) {
UNCOV
59
  try {
×
UNCOV
60
    const { controlId, createdAt } = req.params;
×
61

UNCOV
62
    const startOfDay = new Date(createdAt);
×
63
    startOfDay.setUTCHours(0, 0, 0, 0);
×
64

UNCOV
65
    const endOfDay = new Date(createdAt);
×
66
    endOfDay.setUTCHours(23, 59, 59, 999);
×
67

UNCOV
68
    const computations = await models.Computation.findAll({
×
69
      where: {
70
        controlId,
71
        createdAt: {
72
          [Op.between]: [startOfDay, endOfDay],
73
        },
74
      },
75
    });
76

UNCOV
77
    res.status(200).json(computations);
×
78
  } catch (error) {
NEW
79
    handleControllerError(res, error, 'Failed to get computation by control ID and creation date');
×
80
  }
81
}
82

83
export async function setComputeIntervalBytControlIdAndCreationDate(req, res) {
NEW
84
  const { controlId } = req.params;
×
NEW
85
  const { from, to } = req.body;
×
86

NEW
87
  try {
×
88
    if (!from || !to) {
×
NEW
89
      return res.status(400).json({ error: '"from" and "to" are required in body' });
×
90
    }
NEW
91
    const [updated] = await models.Computation.update(
×
92
      { period: { from, to } },
93
      {
94
        where: {
95
          controlId,
96
          [Op.and]: [
97
            // Use Sequelize.literal to explicitly extract the 'from' value as text
98
            // and then cast it to TIMESTAMPTZ for comparison.
99
            Sequelize.where(
100
              Sequelize.cast(Sequelize.literal("period->>'from'"), 'TIMESTAMPTZ'),
101
              { [Op.gte]: from }
102
            ),
103
            // Do the same for the 'to' value.
104
            Sequelize.where(
105
              Sequelize.cast(Sequelize.literal("period->>'to'"), 'TIMESTAMPTZ'),
106
              { [Op.lte]: to }
107
            )
108
          ]
109
        },
110
      }
111
    );
NEW
112
    if (updated === 0) {
×
NEW
113
      return res.status(404).json({ message: 'No computations found for the given controlId' });
×
114
    }
115

NEW
116
    return res.status(204).json({ message: `${updated} computations updated.` });
×
117
  } catch (err) {
NEW
118
    return res.status(500).json({ error: err.message });
×
119
  }
120
};
121

122
export async function createComputation(req, res) {
123
  try {
6✔
124
    const { metric, config } = req.body;
6✔
125
    const { validation, textError } = checkRequiredProperties(metric, [
6✔
126
      'endpoint',
127
      'params',
128
    ]);
129
    if (!validation) {
6✔
130
      return res.status(400).json({ error: textError });
3✔
131
    }
132
    const endpoint = `/${API_PREFIX}${metric.endpoint}`;
3✔
133
    const computationId = uuidv4();
3✔
134
    const { end: to, ...restWindow } = metric.window;
3✔
135
    const params = {
3✔
136
      computationGroup: computationId,
137
      backendUrl: config.backendUrl,
138
      ...metric.params,
139
      scope: metric.scope,
140
      to,
141
      ...restWindow,
142
    };
143
    const headers = {
3✔
144
      'x-access-token': req.cookies.accessToken,
145
    };
146
    const response = await nodered.post(endpoint, params, { headers });
3✔
147
    if (response.status !== 200) {
2✔
148
      return res
1✔
149
        .status(400)
150
        .json({ message: 'Something went wrong when calling Node-RED' });
151
    }
152
    res.status(201).json({
1✔
153
      code: 201,
154
      message: 'OK',
155
      computation: `${API_PREFIX}/computations/${computationId}`,
156
    });
157
  } catch (error) {
158
    handleControllerError(res, error, 'Failed to create computation');
1✔
159
  }
160
}
161

162
export async function bulkCreateComputations(req, res) {
163
  try {
4✔
164
    const { computations, done } = req.body;
4✔
165
    if (!Array.isArray(computations) || computations.length === 0) {
4✔
166
      return res.status(400).json({ error: 'Invalid computations' });
1✔
167
    }
168
    const { validation, textError } = checkRequiredProperties(computations[0], [
3✔
169
      'computationGroup',
170
    ]);
171
    if (!validation) {
3✔
172
      return res.status(400).json({ error: textError });
1✔
173
    }
174
    const newComputations = await models.Computation.bulkCreate(computations);
2✔
175
    if (done) {
1!
176
      const computationGroup = computations[0].computationGroup;
1✔
177
      await redis.set(computationGroup, true);
1✔
178
    }
179
    res.status(201).json(newComputations);
1✔
180
  } catch (error) {
181
    handleControllerError(res, error, 'Failed to create computations');
1✔
182
  }
183
}
184

185
export async function deleteComputations(req, res) {
186
  try {
2✔
187
    await models.Computation.destroy({ where: {} });
2✔
188
    res.status(204).end();
1✔
189
  } catch (error) {
190
    handleControllerError(res, error, 'Failed to delete computations');
1✔
191

192
  }
193
}
194

195
export async function deleteComputationByControlId(req, res) {
UNCOV
196
  try {
×
UNCOV
197
    const { controlId } = req.params;
×
NEW
198
    const deletedCount = await models.Computation.destroy({ where: { controlId } });
×
NEW
199
    if (deletedCount === 0) {
×
NEW
200
      return res.status(404).json({ message: 'No computations found to delete' });
×
201
    }
UNCOV
202
    res.status(204).end();
×
203
  } catch (error) {
NEW
204
    handleControllerError(res, error, 'Failed to delete computation by control ID');
×
205
  }
206
}
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