Compare commits

...

10 Commits

Author SHA1 Message Date
Sandy Ingram
681e6b29cf chore(grading): adding group grades and feedback 2025-07-08 20:58:57 +02:00
286b05ad57 Merge branch 'feat/test-env'
add test environment

See merge request team-raclette/project-softweng!23
2025-06-24 22:07:33 +02:00
35225ada3c fix(pico-sensor): path relative to root of project as GitLab need it
GitLab need a path from the root of the project even it's in a sub-folder

Signed-off-by: Klagarge <remi@heredero.ch>
2025-06-24 21:38:54 +02:00
b88f24385d doc: add test environment section to README
Signed-off-by: Klagarge <remi@heredero.ch>
2025-06-24 21:26:09 +02:00
9c5840f400 doc: update contributor roles in README
Signed-off-by: Klagarge <remi@heredero.ch>
2025-06-24 21:26:09 +02:00
6e62e28be6 feat(gateway): add test routes
Return 100 last values to be sure that test have some values
Need to be refactor to merge both function. Now it's clearly a mess with duplicate code.

Signed-off-by: Klagarge <remi@heredero.ch>
2025-06-24 21:26:09 +02:00
caa29f2d25 feat(test): add test environnement
remove useless environnement variable

Signed-off-by: Klagarge <remi@heredero.ch>
2025-06-24 21:26:07 +02:00
c103e8c6b4 refactor: tiny polish
remove useless ports
add space

Signed-off-by: Klagarge <remi@heredero.ch>
2025-06-24 21:25:45 +02:00
Yann Sierro
25c999d143 Merge branch '37-fix-ci-e2e'
test(webapp): fix CI test E2E

Closes #37

See merge request team-raclette/project-softweng!22
2025-06-24 19:16:49 +00:00
fastium
b03368aa6f test(webapp): update documentation
- Change the description about the environment test
2025-06-24 21:13:51 +02:00
10 changed files with 210 additions and 18 deletions

View File

@@ -8,6 +8,4 @@ MQTT_USERNAME=
MQTT_PASSWORD=
REST_USERNAME=
REST_PASSWORD=
REST_URL=
REST_PAGE=
CYPRESS_TEST_URL=

View File

@@ -1,5 +1,6 @@
stages:
- gateway-build
- web-app-build
- web-app-tests

View File

@@ -79,14 +79,23 @@ Credentials are available [on request](mailto:remi@heredero.ch).
Details in [pico-sensor/README.md](pico-sensor/README.md).
## Test environment
[(Back to top)](#table-of-contents)
A partial test environment is deployed on the server. <br>
Web app has a second container with the same code but made a GET request on `/test` instead of `/raclette` for the
development environment. The gateway include a route to `/test` that return the 100 last measurement instead of the
last 24h. Has everything else is still a development environment, the database is the same for the test and
development environment.
# Authors
[(Back to top)](#table-of-contents)
* [Sylvan Arnold](https://github.com/Sylvan22)
* [Rémi Heredero](https://github.com/Klagarge)
* [Yann Sierro](https://github.com/Fastium)
* [Sylvan Arnold](https://github.com/Sylvan22) - Mainly pico-sensor part
* [Rémi Heredero](https://github.com/Klagarge) - Mainly gateway and docker part
* [Yann Sierro](https://github.com/Fastium) - Mainly web-app part

View File

@@ -22,8 +22,6 @@ services:
image: registry.forge.hefr.ch/team-raclette/project-softweng/gateway:latest
container_name: gateway
restart: unless-stopped
ports:
- "8080:8080"
environment:
- INFLUXDB_TOKEN=$INFLUXDB_TOKEN
- INFLUXDB_ORG=$INFLUXDB_ORG
@@ -105,15 +103,11 @@ services:
image: registry.forge.hefr.ch/team-raclette/project-softweng/web-app:latest
container_name: web-app
restart: unless-stopped
ports:
- "8080:8080"
environment:
- VUE_APP_REST_USER=$REST_USERNAME
- VUE_APP_REST_PASSWORD=$REST_PASSWORD
- VUE_APP_REST_URL=$REST_URL
- VUE_APP_REST_PAGE=$REST_PAGE
- VUE_APP_REST_URL=rest.mse.kb28.ch
- VUE_APP_REST_PAGE=raclette
labels:
- "traefik.enable=true"
- "traefik.http.routers.web-app-http.entrypoints=http"
@@ -125,3 +119,24 @@ services:
- "traefik.http.services.web-app-https.loadbalancer.server.port=8080"
- "com.centurylinklabs.watchtower.enable=true"
web-app-test:
image: registry.forge.hefr.ch/team-raclette/project-softweng/web-app:latest
container_name: web-app-test
restart: unless-stopped
environment:
- VUE_APP_REST_USER=$REST_USERNAME
- VUE_APP_REST_PASSWORD=$REST_PASSWORD
- VUE_APP_REST_URL=rest.mse.kb28.ch
- VUE_APP_REST_PAGE=test
labels:
- "traefik.enable=true"
- "traefik.http.routers.web-app-test-http.entrypoints=http"
- "traefik.http.routers.web-app-test-http.rule=Host(`app-test.mse.kb28.ch`)"
- "traefik.http.middlewares.web-app-test-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.web-app-test-https.entrypoints=https"
- "traefik.http.routers.web-app-test-https.rule=Host(`app-test.mse.kb28.ch`)"
- "traefik.http.routers.web-app-test-https.tls.certResolver=letsencrypt"
- "traefik.http.services.web-app-test-https.loadbalancer.server.port=8080"
- "com.centurylinklabs.watchtower.enable=true"

View File

@@ -22,6 +22,7 @@ type Gateway struct {
mqtt paho.Client
rest *gin.Engine
restGroup *gin.RouterGroup
testGroup *gin.RouterGroup
accounts gin.Accounts
influx influxdb2.Client
influxApi api.WriteAPIBlocking
@@ -100,6 +101,7 @@ func (gh *Gateway) createRestGateway() {
// Create a new router group with basic authentication for /raclette
gh.restGroup = gh.rest.Group("/raclette", gin.BasicAuth(gh.accounts))
gh.testGroup = gh.rest.Group("/test", gin.BasicAuth(gh.accounts))
// Define the route for the publish command
gh.restGroup.POST("", func(c *gin.Context) {
@@ -129,6 +131,14 @@ func (gh *Gateway) createRestGateway() {
c.JSON(http.StatusInternalServerError, gin.H{"error": ret.Error()})
}
})
// Define the route for the publish command (GET)
gh.testGroup.GET("", func(c *gin.Context) {
ret := gh.requestInfluxTest(c)
if ret != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": ret.Error()})
}
})
}
// createMQTTGateway initializes the MQTT client and sets up subscriptions.

View File

@@ -68,6 +68,7 @@ func (gh *Gateway) requestInflux(c *gin.Context) error {
|> filter(fn: (r) => r["user"] == %q)
|> filter(fn: (r) => r["room"] == %q)
|> filter(fn: (r) => r["device"] == %q)
|> aggregateWindow(every: 5m, fn: mean, createEmpty: false)
|> sort(columns: ["_time"], desc: true)
`, INFLUXDB_BUCKET, MEASUREMENT_NAME, user, room, device)
results, err := queryAPI.Query(context.Background(), query)
@@ -101,3 +102,83 @@ func (gh *Gateway) requestInflux(c *gin.Context) error {
c.JSON(http.StatusOK, values)
return nil
}
func (gh *Gateway) requestInfluxTest(c *gin.Context) error {
// Get the user from the authenticated context
//userId := c.MustGet(gin.AuthUserKey).(string)
// Get room and device from the query parameters
user, ret := c.GetQuery("user")
if !ret {
return errors.New("no user found")
}
room, ret := c.GetQuery("room")
if !ret {
return errors.New(`no room found`)
}
device, ret := c.GetQuery("device")
if !ret {
return errors.New(`no device found`)
}
// Get env variables and set default values if not set
INFLUXDB_ORG, ok := os.LookupEnv("INFLUXDB_ORG")
if !ok {
log.Error("INFLUXDB_ORG not set, using default value: raclette")
INFLUXDB_ORG = "raclette"
}
INFLUXDB_BUCKET, ok := os.LookupEnv("INFLUXDB_BUCKET")
if !ok {
log.Error("INFLUXDB_BUCKET not set, using default value: raclette")
INFLUXDB_BUCKET = "raclette"
}
queryAPI := gh.influx.QueryAPI(INFLUXDB_ORG)
MEASUREMENT_NAME, ok := os.LookupEnv("MEASUREMENT_NAME")
if !ok {
log.Error("MEASUREMENT_NAME not set, using default value: softweng")
MEASUREMENT_NAME = "THC"
}
// The Flux query uses a large range (-1000d) and aggregates the latest values.
// This ensures we always get the most recent data, even if the database contains old entries.
query := fmt.Sprintf(`from(bucket: %q)
|> range(start: -1000d)
|> filter(fn: (r) => r["_measurement"] == %q)
|> filter(fn: (r) => r["user"] == %q)
|> filter(fn: (r) => r["room"] == %q)
|> filter(fn: (r) => r["device"] == %q)
|> sort(columns: ["_time"], desc: true)
|> limit(n: 100)
`, INFLUXDB_BUCKET, MEASUREMENT_NAME, user, room, device)
results, err := queryAPI.Query(context.Background(), query)
if err != nil {
log.Fatal(err)
}
type measurement = map[string]interface{}
var values []measurement
for results.Next() {
m := measurement{}
record := results.Record()
field := record.Field()
value := record.Value()
m["time"] = record.Time()
m["value"] = value.(float64)
if field == "temperature" {
m["type"] = "temperature"
}
if field == "humidity" {
m["type"] = "humidity"
}
values = append(values, m)
fmt.Println(results.Record())
}
if err := results.Err(); err != nil {
log.Fatal(err)
}
c.JSON(http.StatusOK, values)
return nil
}

View File

@@ -0,0 +1,72 @@
# Group 4 - project management and web part feedback
## Gitlab board and project management
### Requirements
1. Check the project description and split project requirements into gitlab iterations or gitlab milestones.
2. Define a couple of users stories following the example and structure provided in the course slides (put them in the gitlab board backlog at this stage) as issues in gitlab board.
3. Assign every story to one of your gitlab iterations (or gitlab milestones). 
4. Split user stories into tasks (add the tasks to the gitlab board descriptions.
5. Estimate 2 user stories tasks using poker cards. Timeless story point estimations are encouraged. Nevertheless, and as agreed in class, If  your group wishes to proceed with a time-based estimation then it's OK (for as long as at least one user story is also estimated in terms of timeless story points (per task and added together for the user story and the result should be put in its description). Add story points estimation per task and user story to the corresponding issue description (for 1 to 2 user stories only).
6. Discuss differences (and document the process: duration, points of disagreements, adjustments, clarifications, perceived exercice usefulness). This documentation is added as a file in your gitlab repo (under the name: poker_planning_retro.md). 
7. Once team reaches agreement on task estimation, sum up all tasks points and add them to the chosen user story “weight” on gitlab.
8. Use your gitlab board during the semester in a consistent way to track issue progress. At the end of the semester, the board has to be up to date with development (e.g. close finished tasks, etc).
### Grade feedback : 3.5/3.5
* Pokercard planning: All won't change there vote, they are convice it's this level of difficulty. => amusing to some extent! but usually you are expected to reach a consensus and bring convincing facts to reach it.
* Thank you for the precise reporting and clearly mentioning your metric.
* Very good usage of the gitlab boards with labels
* Convention commits : ok (well done)
* Links between issues and commits: ok
## Design
### Requirements
In this part, you are expected to deliver a documentation of your projet's design specifications, with as much as possible reference to what we have seen in this lecture. Those of you who attended this lecture, were shown sample documentations that meet our expectations. Your documentation should typically specify/include:
* Which specific architecture styles and architecture and design patterns you will be using and make sure you refer to them correctly (a must have). Use the course slides as your reference.
* The C4 model and/or the Kruchten's 4+ 1 views, to represent your design (strongly encouraged).
* a couple of other complementary diagrams are also expected to illustrate/represent your projet's global architecture, how systems components will communicate, your technology stack (which will still evolve as we progress throughout the course), deployment diagrams (Kruchten's 4+1 views can typically be useful for this matter). Use UML only if you are confortable with it, otherwise it is not imposed.
* It's about the quality not the quantity; aim for a clear, concise, and precise documentation. 
* Once your design is specified, take some time to update your gitlab boards, prepare user stories (and corresponding tasks).
* As you design your components and your code, keep also in mind composition (interfaces vs inheritance), and aspects related to design by contract.
* Feel free to send me a draft until March 4, to receive a formative (non graded) feedback, to make sure sure your submissions meets expectations.
* Your first documentation draft and the final one must be in your gitlab repository in the main branch, in a folder named "design" (in pdf or mark down format).
### Grade feedback : 2/3.5
* Very good contexte and container diagrams
* Class diagram ok too but couldnt find a component diagram
* Plus I didnt find any information regarding architecture styles and patterns identified and used
## Dockerized app
### Requirements
Your project must be fully dockerized. 
* Define a docker image per main component.
* Then use docker-compose to run services from your docker images. Propose a complete solution for services that depend on one another.
* Volumes or other types of storage drivers might also be useful. The system must be easy to launch locally with simple "docker-compose up command".
* Your dockerized system should be run on gitlab (which might raise some challenge, when it comes to automatic deployment, demos/hints will be given in subsequent lectures).
### Grade feedback : /3
*
## Web app Testing
### Requirements
While it should be clear to all of us, that test coverage is important in real working environments, in this project specifically, the focus is not on coverage but on be able to differentiate different types of tests and experiment with different testing technologies. So each group is expect to:
* Implement 2 Unit tests frontend (using cypress and with mock data).
* Implement 1 Integration test frontend/backend ou a full E2E (end-to-end) test for a key feature: for example getting a specific measurements saved in the backend (in files our case) and to be displayed on the frontend Web page.
* Tests should be called at every commit; a CI/CD pipeline needs to be using gitlab.yml (and relying on provided examples).
* Test cases must be documented in a markdown file named web-tests.md, clarifying for each tests: the 1) test purpose, 2) the test type (unit, integration, E2E) as well as 3) the type of testing double chosen to conduct the test.
### Grade feedback : /4
*
## Webapp CI/CD
### Requirements
* Build a CI/CD pipeline for your dockerized project, especially in relation to deploying the required image on gitlab and using docker-in-docker
* Tests should be called at every commit; a CI/CD pipeline needs to be using gitlab.yml
### Grade feedback : /3
Total Project Grade (with the Embedded Part) over 24: 19.5

View File

@@ -70,7 +70,7 @@ RUN \
ENV PICO_SDK_PATH=/apps/pico-sdk/
# Patch the SDK for semihosting file I/O (needed for gcov)
COPY newlib_interface.c.patched ${PICO_SDK_PATH}/src/rp2_common/pico_clib_interface/newlib_interface.c
COPY ./pico-sensor/newlib_interface.c.patched ${PICO_SDK_PATH}/src/rp2_common/pico_clib_interface/newlib_interface.c
# Build picotool so we don't have to build it for the project
RUN \

View File

@@ -65,7 +65,10 @@ npm run build
<p align="center"> ![Class Diagram](docs/class-diagramm-web-app.svg)
### Links
## Tests
Details in [web-app/test.md](web-app/test.md)
# Links
See [Configuration Reference](https://cli.vuejs.org/config/).\
See [Vue.js](https://vuejs.org/guide/introduction.html).\
See [chartjs](https://www.chartjs.org/docs/latest/).\

View File

@@ -65,9 +65,6 @@ npx cypress open
- Tests fetching timeseries data with a valid user-room-device selection, ensuring the main chart is displayed and no "no data" message appears.
- Tests fetching timeseries data with an invalid user-room-device selection (e.g., changing the user to "Sylvan"), ensuring the main chart is not displayed and the "no data" message is shown.
#### Important Notes
The E2E tests use a web-app development image deployed on https://app.mse.kb28.ch/ for the moment. Because there is a problem with the service image in the CI. The environment variables cannot be set correctly to the job's service. An issue is opened to fix this problem: [#37].
### Unit Tests
- **Serie Component**:
@@ -76,3 +73,9 @@ The E2E tests use a web-app development image deployed on https://app.mse.kb28.c
- Ensures the label "Unknown" is returned for unknown types.
- Verifies that temperature and humidity data are formatted correctly by `getSerie()`, including label, color, and data structure.
- Ensures unknown types are handled appropriately in `getSerie()`, defaulting to the correct color and axis.
## Test in CI
All test are covered by the CI pipeline.
The unit tests are run on every commit and doesn't require any specific resources.
The E2E tests are run too on every commit, but they require a test URL to be set in the environment variable `CYPRESS_TEST_URL` which provide an access to a test instance of the web-app. Actually, these tests are running on the test development instance at `https://app-dev.mse.kb28.ch/`.