Skip to content
This repository was archived by the owner on Mar 22, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
b3481c4
new branch test
SimonPergel Jan 21, 2025
a6f269d
new test file added
SimonPergel Jan 22, 2025
1223c78
Corrected the calculating function
Jan 22, 2025
f5ded85
Fixed API calls to only do it one time with several instances?
Jan 23, 2025
a146df7
Small fix to make the instances sleep a bit
Jan 23, 2025
5f9f8e4
Merge branch 'master' into Comfortstat
Jan 23, 2025
7ca06c1
Added Alex fix to Jan's code
Jan 23, 2025
a95b553
Ran go mod tidy
Jan 23, 2025
cc8ff43
more tests added
SimonPergel Jan 23, 2025
d6e6a1f
added more tests for the getters in things.go
SimonPergel Jan 23, 2025
df3d27c
Merge branch 'master' into Comfortstat
Jan 24, 2025
8fb3b25
Fixed API to not be called in init_template
Jan 24, 2025
6bd7214
fixed the set-functions in thing.go and added more tests
SimonPergel Jan 24, 2025
8b9f869
Added working tests for getters och setters functions
SimonPergel Jan 24, 2025
260cba2
added some more tests
SimonPergel Jan 24, 2025
2b73ffa
Merge remote-tracking branch 'origin/workflow' into Comfortstat
SimonPergel Jan 24, 2025
ffe9cc1
adding tests plus cleaning up things.go
SimonPergel Jan 28, 2025
95fac3b
trying to test processfeedbackLoop
SimonPergel Jan 28, 2025
1ea88dc
trying to test processfeedbackLoop
SimonPergel Jan 28, 2025
db73ae6
cleand up some things
SimonPergel Jan 29, 2025
d453a4a
working in GetapiPrice test
SimonPergel Jan 29, 2025
39b6aa0
cleaned up some comments and added some comments to parts with no ex…
SimonPergel Jan 29, 2025
f5fef4b
More tests
SimonPergel Jan 29, 2025
2224f3d
not there yet, but the push tests are woring fine
SimonPergel Jan 30, 2025
41add31
not there yet, but the pushed tests are working fine
SimonPergel Jan 30, 2025
1281db4
more tests
SimonPergel Jan 30, 2025
2cf76b6
fixes testing bad body
lmas Jan 30, 2025
b2dbfe2
test for things.go is completed
SimonPergel Jan 30, 2025
29b556d
added working test of the GET parts
SimonPergel Jan 31, 2025
2521e46
cleaned up and added some comments to clarify diffrent parts
SimonPergel Feb 2, 2025
161e287
added test for the PUT part in Comfortstat.go
SimonPergel Feb 3, 2025
8b7971b
Updated newUnitAsset
SimonPergel Feb 3, 2025
2434825
updated getapiprice function to match linter tests
SimonPergel Feb 3, 2025
64b925d
added a part that validate the url in getApiPricedata
SimonPergel Feb 4, 2025
79cab32
added error handling in getApiPricedata
SimonPergel Feb 4, 2025
de331da
added error handling in the pricefeedbackloop
SimonPergel Feb 4, 2025
9b0086a
added error handling in pricefeedbackloop
SimonPergel Feb 4, 2025
9a8d9af
cleaned up some log.prints in the set-functions
SimonPergel Feb 4, 2025
01af2e1
moved the check for the statuscode to the right place, before reading…
SimonPergel Feb 4, 2025
e4dca4e
fixed so that i run the tests directly after the fuction call
SimonPergel Feb 4, 2025
4e22141
changed the name to a more suitable name
SimonPergel Feb 4, 2025
7ccffce
Reverts " changed the name to a more suitable name"
lmas Feb 4, 2025
108b245
changed to a more suitable name
SimonPergel Feb 4, 2025
9308c0a
replaced the sleep in things.go to Comfortstat.go
Feb 5, 2025
2e32040
Added user temperature to the comfortstat
Feb 7, 2025
4897efb
added some comments and removed emty lines
SimonPergel Feb 7, 2025
27915d7
fixed the setter-methods
SimonPergel Feb 7, 2025
f4667fe
Added special case for the user temp
Feb 10, 2025
dfb65b5
Changed userTemp to UserTemp so it is exported as json
Feb 10, 2025
aed44a4
cleaned up some emty rows and created some explanatory comments
SimonPergel Feb 11, 2025
e68e039
Resolved all the comments in the review part
SimonPergel Feb 11, 2025
ed7d584
Removed one feedbackloop as it was unnecessary, fixed the hourly pric…
Feb 11, 2025
9b7a64b
added so the user can choose price region
SimonPergel Feb 12, 2025
885c07a
cleand up the new switchRegion function and added some comments
SimonPergel Feb 12, 2025
b59a0fe
Added tests for the newly implemented features(Usertemp and REgion c…
SimonPergel Feb 13, 2025
9cc8398
Cleand up the new funtions that have been implemented
SimonPergel Feb 13, 2025
f4630b9
Added files for the SunButton system
Feb 25, 2025
6b5e824
Added test files for the SunButton files
gabaxh Feb 25, 2025
1e7fb95
Correction in test code
gabaxh Feb 25, 2025
4cc3282
Fixed the test for ButtonStatus
gabaxh Feb 25, 2025
8e3f494
added more tests regarding the getApiData
SimonPergel Feb 28, 2025
8b0d23c
fixed conflicts from dev -> Comfortstat
SimonPergel Mar 4, 2025
21388d5
Fixed misspelling
SimonPergel Mar 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions SunButton/SunButton.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package main

import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"time"

"github.com/sdoque/mbaigo/components"
"github.com/sdoque/mbaigo/usecases"
)

func main() {
// Prepare for graceful shutdown
ctx, cancel := context.WithCancel(context.Background()) // Create a context that can be cancelled
defer cancel() // Make sure all paths cancel the context to avoid context leak

// Instantiate the System
sys := components.NewSystem("SunButton", ctx)

// Instantiate the Capsule
sys.Husk = &components.Husk{
Description: "Is a controller for a consumed button based on a consumed time of day. Powered by SunriseSunset.io",
Certificate: "ABCD",
Details: map[string][]string{"Developer": {"Arrowhead"}},
ProtoPort: map[string]int{"https": 0, "http": 8670, "coap": 0},
InfoLink: "https://github.com/lmas/d0020e_code/tree/master/SunButton",
}

// Instantiate a template unit asset
assetTemplate := initTemplate()
assetName := assetTemplate.GetName()
sys.UAssets[assetName] = &assetTemplate

// Configure the system
rawResources, servsTemp, err := usecases.Configure(&sys)
if err != nil {
log.Fatalf("Configuration error: %v\n", err)
}
sys.UAssets = make(map[string]*components.UnitAsset) // Clear the unit asset map (from the template)
for _, raw := range rawResources {
var uac UnitAsset
if err := json.Unmarshal(raw, &uac); err != nil {
log.Fatalf("Resource configuration error: %+v\n", err)
}
ua, startup := newUnitAsset(uac, &sys, servsTemp)
startup()
sys.UAssets[ua.GetName()] = &ua
}

// Generate PKI keys and CSR to obtain a authentication certificate from the CA
usecases.RequestCertificate(&sys)

// Register the (system) and its services
usecases.RegisterServices(&sys)

// Start the http handler and server
go usecases.SetoutServers(&sys)

// Wait for shutdown signal and gracefully close properly goroutines with context
<-sys.Sigs // Wait for a SIGINT (Crtl+C) signal
fmt.Println("\nShutting down system", sys.Name)
cancel() // Cancel the context, signaling the goroutines to stop
time.Sleep(2 * time.Second) // Allow the go routines to be executed, which might take more time then the main routine to end
}

// Serving handles the resource services. NOTE: It expects those names from the request URL path
func (t *UnitAsset) Serving(w http.ResponseWriter, r *http.Request, servicePath string) {
switch servicePath {
case "ButtonStatus":
t.httpSetButton(w, r)
case "Latitude":
t.httpSetLatitude(w, r)
case "Longitude":
t.httpSetLongitude(w, r)
default:
http.Error(w, "Invalid service request [Do not modify the services subpath in the configuration file]", http.StatusBadRequest)
}
}

// All these functions below handles HTTP "PUT" or "GET" requests to modify or retrieve the latitude and longitude and the state of the button
// For the PUT case - the "HTTPProcessSetRequest(w, r)" is called to prosses the data given from the user and if no error,
// call the set functions in thing.go with the value witch updates the value in the struct
func (rsc *UnitAsset) httpSetButton(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "PUT":
sig, err := usecases.HTTPProcessSetRequest(w, r)
if err != nil {
http.Error(w, "request incorrectly formatted", http.StatusBadRequest)
return
}
rsc.setButtonStatus(sig)
case "GET":
signalErr := rsc.getButtonStatus()
usecases.HTTPProcessGetRequest(w, r, &signalErr)
default:
http.Error(w, "Method is not supported.", http.StatusNotFound)
}
}

func (rsc *UnitAsset) httpSetLatitude(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "PUT":
sig, err := usecases.HTTPProcessSetRequest(w, r)
if err != nil {
http.Error(w, "request incorrectly formatted", http.StatusBadRequest)
return
}
rsc.setLatitude(sig)
case "GET":
signalErr := rsc.getLatitude()
usecases.HTTPProcessGetRequest(w, r, &signalErr)
default:
http.Error(w, "Method is not supported.", http.StatusNotFound)
}
}

func (rsc *UnitAsset) httpSetLongitude(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "PUT":
sig, err := usecases.HTTPProcessSetRequest(w, r)
if err != nil {
http.Error(w, "request incorrectly formatted", http.StatusBadRequest)
return
}
rsc.setLongitude(sig)
case "GET":
signalErr := rsc.getLongitude()
usecases.HTTPProcessGetRequest(w, r, &signalErr)
default:
http.Error(w, "Method is not supported.", http.StatusNotFound)
}
}
215 changes: 215 additions & 0 deletions SunButton/SunButton_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package main

import (
"bytes"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
)

func TestHttpSetButton(t *testing.T) {
ua := initTemplate().(*UnitAsset)

// Good case test: GET
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "http://172.30.106.39:8670/SunButton/Button/ButtonStatus", nil)
goodStatusCode := 200
ua.httpSetButton(w, r)
// calls the method and extracts the response and save is in resp for the upcoming tests
resp := w.Result()
if resp.StatusCode != goodStatusCode {
t.Errorf("expected good status code: %v, got %v", goodStatusCode, resp.StatusCode)
}
body, _ := io.ReadAll(resp.Body)
// this is a simple check if the JSON response contains the specific value/unit/version
value := strings.Contains(string(body), `"value": 0.5`)
unit := strings.Contains(string(body), `"unit": "bool"`)
version := strings.Contains(string(body), `"version": "SignalA_v1.0"`)
// check results from above
if value != true {
t.Errorf("expected the statement to be true!")
}
if unit != true {
t.Errorf("expected the unit statement to be true!")
}
if version != true {
t.Errorf("expected the version statement to be true!")
}

//Godd test case: PUT
// creates a fake request body with JSON data
w = httptest.NewRecorder()
fakebody := bytes.NewReader([]byte(`{"value": 0, "unit": "bool", "version": "SignalA_v1.0"}`)) // converts the Jason data so it can be read
r = httptest.NewRequest("PUT", "http://172.30.106.39:8670/SunButton/Button/ButtonStatus", fakebody) // simulating a put request from a user to update the button status
r.Header.Set("Content-Type", "application/json") // basic setup to prevent the request to be rejected.
ua.httpSetButton(w, r)

// save the response and read the body
resp = w.Result()
if resp.StatusCode != goodStatusCode {
t.Errorf("expected good status code: %v, got %v", goodStatusCode, resp.StatusCode)
}

//BAD case: PUT, if the fake body is formatted incorrectly

// creates a fake request body with JSON data
w = httptest.NewRecorder()
fakebody = bytes.NewReader([]byte(`{"123, "unit": "bool", "version": "SignalA_v1.0"}`)) // converts the Jason data so it can be read
r = httptest.NewRequest("PUT", "http://172.30.106.39:8670/SunButton/Button/ButtonStatus", fakebody) // simulating a put request from a user to update the button status
r.Header.Set("Content-Type", "application/json") // basic setup to prevent the request to be rejected.
ua.httpSetButton(w, r)
// save the response and read the body
resp = w.Result()
if resp.StatusCode == goodStatusCode {
t.Errorf("expected bad status code: %v, got %v", goodStatusCode, resp.StatusCode)
}
// Bad test case: default part of code
// force the case to hit default statement but alter the method
w = httptest.NewRecorder()
r = httptest.NewRequest("123", "http://172.30.106.39:8670/SunButton/Button/ButtonStatus", nil)
// calls the method and extracts the response and save is in resp for the upcoming tests
ua.httpSetButton(w, r)
resp = w.Result()
if resp.StatusCode != http.StatusNotFound {
t.Errorf("Expected the status to be bad but got: %v", resp.StatusCode)
}
}

func TestHttpSetLatitude(t *testing.T) {
ua := initTemplate().(*UnitAsset)

//Godd test case: PUT
// creates a fake request body with JSON data
w := httptest.NewRecorder()
fakebody := bytes.NewReader([]byte(`{"value": 65.584816, "unit": "Degrees", "version": "SignalA_v1.0"}`)) // converts the Jason data so it can be read
r := httptest.NewRequest("PUT", "http://172.30.106.39:8670/SunButton/Button/Latitude", fakebody) // simulating a put request from a user to update the latitude
r.Header.Set("Content-Type", "application/json") // basic setup to prevent the request to be rejected.
goodStatusCode := 200
ua.httpSetLatitude(w, r)

// save the response and read the body
resp := w.Result()
if resp.StatusCode != goodStatusCode {
t.Errorf("expected good status code: %v, got %v", goodStatusCode, resp.StatusCode)
}

//BAD case: PUT, if the fake body is formatted incorrectly

// creates a fake request body with JSON data
w = httptest.NewRecorder()
fakebody = bytes.NewReader([]byte(`{"123, "unit": "Degrees", "version": "SignalA_v1.0"}`)) // converts the Jason data so it can be read
r = httptest.NewRequest("PUT", "http://172.30.106.39:8670/SunButton/Button/Latitude", fakebody) // simulating a put request from a user to update the latitude
r.Header.Set("Content-Type", "application/json") // basic setup to prevent the request to be rejected.
ua.httpSetLatitude(w, r)
// save the response and read the body
resp = w.Result()
if resp.StatusCode == goodStatusCode {
t.Errorf("expected bad status code: %v, got %v", goodStatusCode, resp.StatusCode)
}
//Good test case: GET
w = httptest.NewRecorder()
r = httptest.NewRequest("GET", "http://172.30.106.39:8670/SunButton/Button/Latitude", nil)
goodStatusCode = 200
ua.httpSetLatitude(w, r)

// save the response and read the body
resp = w.Result()
if resp.StatusCode != goodStatusCode {
t.Errorf("expected good status code: %v, got %v", goodStatusCode, resp.StatusCode)
}
body, _ := io.ReadAll(resp.Body)
// this is a simple check if the JSON response contains the specific value/unit/version
value := strings.Contains(string(body), `"value": 65.584816`)
unit := strings.Contains(string(body), `"unit": "Degrees"`)
version := strings.Contains(string(body), `"version": "SignalA_v1.0"`)
// check the result from above
if value != true {
t.Errorf("expected the statement to be true!")
}
if unit != true {
t.Errorf("expected the unit statement to be true!")
}
if version != true {
t.Errorf("expected the version statement to be true!")
}
// bad test case: default part of code
// force the case to hit default statement but alter the method
w = httptest.NewRecorder()
r = httptest.NewRequest("666", "http://172.30.106.39:8670/SunButton/Button/Latitude", nil)
ua.httpSetLatitude(w, r)
resp = w.Result()
if resp.StatusCode != http.StatusNotFound {
t.Errorf("expected the status to be bad but got: %v", resp.StatusCode)
}
}

func TestHttpSetLongitude(t *testing.T) {
ua := initTemplate().(*UnitAsset)
//Godd test case: PUT

// creates a fake request body with JSON data
w := httptest.NewRecorder()
fakebody := bytes.NewReader([]byte(`{"value": 22.156704, "unit": "Degrees", "version": "SignalA_v1.0"}`)) // converts the Jason data so it can be read
r := httptest.NewRequest("PUT", "http://172.30.106.39:8670/SunButton/Button/Longitude", fakebody) // simulating a put request from a user to update the longitude
r.Header.Set("Content-Type", "application/json") // basic setup to prevent the request to be rejected.
goodStatusCode := 200
ua.httpSetLongitude(w, r)

// save the response and read the body
resp := w.Result()
if resp.StatusCode != goodStatusCode {
t.Errorf("expected good status code: %v, got %v", goodStatusCode, resp.StatusCode)
}
//BAD case: PUT, if the fake body is formatted incorrectly

// creates a fake request body with JSON data
w = httptest.NewRecorder()
fakebody = bytes.NewReader([]byte(`{"123, "unit": "Degrees", "version": "SignalA_v1.0"}`)) // converts the Jason data so it can be read
r = httptest.NewRequest("PUT", "http://172.30.106.39:8670/SunButton/Button/Longitude", fakebody) // simulating a put request from a user to update the min temp
r.Header.Set("Content-Type", "application/json") // basic setup to prevent the request to be rejected.
ua.httpSetLongitude(w, r)

// save the response and read the body
resp = w.Result()
if resp.StatusCode == goodStatusCode {
t.Errorf("expected bad status code: %v, got %v", goodStatusCode, resp.StatusCode)
}
//Good test case: GET
w = httptest.NewRecorder()
r = httptest.NewRequest("GET", "http://172.30.106.39:8670/SunButton/Button/Longitude", nil)
goodStatusCode = 200
ua.httpSetLongitude(w, r)

// save the response and read the body
resp = w.Result()
if resp.StatusCode != goodStatusCode {
t.Errorf("expected good status code: %v, got %v", goodStatusCode, resp.StatusCode)
}
body, _ := io.ReadAll(resp.Body)
// this is a simple check if the JSON response contains the specific value/unit/version
value := strings.Contains(string(body), `"value": 22.156704`)
unit := strings.Contains(string(body), `"unit": "Degrees"`)
version := strings.Contains(string(body), `"version": "SignalA_v1.0"`)
if value != true {
t.Errorf("expected the statement to be true!")
}
if unit != true {
t.Errorf("expected the unit statement to be true!")
}
if version != true {
t.Errorf("expected the version statement to be true!")
}
// bad test case: default part of code

// force the case to hit default statement but alter the method
w = httptest.NewRecorder()
r = httptest.NewRequest("666", "http://172.30.106.39:8670/SunButton/Button/Longitude", nil)
ua.httpSetLongitude(w, r)
//save the response
resp = w.Result()
if resp.StatusCode != http.StatusNotFound {
t.Errorf("expected the status to be bad but got: %v", resp.StatusCode)
}
}
Loading