diff --git a/client.go b/client.go index e904cc5..edc17d4 100644 --- a/client.go +++ b/client.go @@ -12,7 +12,7 @@ import ( "resty.dev/v3" ) -// Client . +// Client represents a MySubaru API client that interacts with the MySubaru API. type Client struct { credentials config.Credentials httpClient *resty.Client @@ -28,7 +28,7 @@ type Client struct { sync.RWMutex } -// New function creates a New MySubaru client +// New function creates a New MySubaru API client func New(config *config.Config) (*Client, error) { client := &Client{ @@ -89,7 +89,7 @@ func New(config *config.Config) (*Client, error) { return client, nil } -// SelectVehicle . +// SelectVehicle selects a vehicle by its VIN. If no VIN is provided, it uses the current VIN. func (c *Client) SelectVehicle(vin string) (*VehicleData, error) { if vin == "" { vin = c.currentVin @@ -117,7 +117,7 @@ func (c *Client) SelectVehicle(vin string) (*VehicleData, error) { return &vd, nil } -// GetVehicles . +// GetVehicles retrieves a list of vehicles associated with the client's account. func (c *Client) GetVehicles() ([]*Vehicle, error) { var vehicles []*Vehicle for _, vin := range c.listOfVins { @@ -131,7 +131,7 @@ func (c *Client) GetVehicles() ([]*Vehicle, error) { return vehicles, nil } -// GetVehicleByVIN . +// GetVehicleByVin retrieves a vehicle by its VIN from the client's list of vehicles. func (c *Client) GetVehicleByVin(vin string) (*Vehicle, error) { var vehicle *Vehicle if slices.Contains(c.listOfVins, vin) { @@ -203,15 +203,15 @@ func (c *Client) GetVehicleByVin(vin string) (*Vehicle, error) { // func getRemoteStartStatus() {} // func getSafetyStatus() {} // func getSubscriptionStatus() {} -// func getClimateData() {} -// func saveClimateSettings() {} +// IsAlive checks if the Client instance is alive func (c *Client) IsAlive() bool { return c.isAlive } -// Exec method executes a Client instance with the API URL +// execute executes an HTTP request based on the method, URL, and parameters provided. func (c *Client) execute(method string, url string, params map[string]string, j bool) (*Response, error) { + c.Lock() // defer timeTrack("[TIMETRK] Executing HTTP Request") var resp *resty.Response var err error @@ -263,6 +263,7 @@ func (c *Client) execute(method string, url string, params map[string]string, j if r, ok := c.parseResponse(resBytes); ok { c.isAlive = true + c.Unlock() return &r, nil } else { if r.DataName == "errorResponse" { @@ -275,16 +276,18 @@ func (c *Client) execute(method string, url string, params map[string]string, j c.logger.Error("request got an error", "request", "execute", "method", method, "url", url, "label", er.ErrorLabel, "descrip[tion", er.ErrorDescription) } c.logger.Error("request got an unknown error", "request", "execute", "method", method, "url", url, "label", er.ErrorLabel, "descrip[tion", er.ErrorDescription) + c.Unlock() return nil, errors.New("request is not successfull, HTTP code: " + resp.Status()) } c.logger.Error("request is not successfull", "request", "execute", "method", method, "url", url, "error", err.Error()) } } c.isAlive = false + c.Unlock() return nil, errors.New("request is not successfull, HTTP code: " + resp.Status()) } -// auth . +// auth authenticates the client with the MySubaru API using the provided credentials. func (c *Client) auth() (*Response, error) { params := map[string]string{ "env": "cloudprod", @@ -306,7 +309,7 @@ func (c *Client) auth() (*Response, error) { return resp, nil } -// parseResponse . +// parseResponse parses the JSON response from the MySubaru API into a Response struct. func (c *Client) parseResponse(b []byte) (Response, bool) { var r Response err := json.Unmarshal(b, &r) @@ -317,7 +320,7 @@ func (c *Client) parseResponse(b []byte) (Response, bool) { return r, true } -// ValidateSession . +// ValidateSession checks if the current session is valid by making a request to the vehicle status API. func (c *Client) ValidateSession() bool { reqURL := MOBILE_API_VERSION + apiURLs["API_VEHICLE_STATUS"] resp, err := c.execute(GET, reqURL, map[string]string{}, false) diff --git a/client_test.go b/client_test.go index ce0eb67..d71c1df 100644 --- a/client_test.go +++ b/client_test.go @@ -11,7 +11,7 @@ import ( "git.savin.nyc/alex/mysubaru/config" ) -func makeConfig(t *testing.T) *config.Config { +func mockConfig(t *testing.T) *config.Config { return &config.Config{ MySubaru: config.MySubaru{ Credentials: config.Credentials{ @@ -29,6 +29,7 @@ func makeConfig(t *testing.T) *config.Config { } } +// mockMySubaruApi creates a mock MySubaru API server for testing. func mockMySubaruApi(t *testing.T, handler http.HandlerFunc) *httptest.Server { // Create a listener with the desired port @@ -176,7 +177,7 @@ func TestNew_Success(t *testing.T) { ts.Start() defer ts.Close() - cfg := makeConfig(t) + cfg := mockConfig(t) msc, err := New(cfg) if err != nil { @@ -235,7 +236,7 @@ func TestSelectVehicle_Success(t *testing.T) { ts.Start() defer ts.Close() - cfg := makeConfig(t) + cfg := mockConfig(t) msc, err := New(cfg) if err != nil { @@ -309,7 +310,7 @@ func TestGetVehicleByVin_Success(t *testing.T) { ts.Start() defer ts.Close() - cfg := makeConfig(t) + cfg := mockConfig(t) msc, err := New(cfg) if err != nil { diff --git a/mysubaru.go b/mysubaru.go index 6bb8106..5f0c111 100644 --- a/mysubaru.go +++ b/mysubaru.go @@ -4,7 +4,7 @@ import ( "encoding/json" ) -// Response . +// Response represents the structure of a response from the MySubaru API. type Response struct { Success bool `json:"success"` // true | false ErrorCode string `json:"errorCode,omitempty"` // string | Error message if Success is false @@ -25,7 +25,7 @@ type Response struct { // Unmarshal . // func (r *Response) Unmarshal(b []byte) {} -// Request . +// Request represents the structure of a request to the MySubaru API. type Request struct { Vin string `json:"vin"` // Pin string `json:"pin"` // diff --git a/utils.go b/utils.go index 2a7ffa4..275cd4b 100644 --- a/utils.go +++ b/utils.go @@ -58,7 +58,7 @@ func vinCheck(vin string) (bool, string) { return valid, retVin } -// transcodeDigits . +// transcodeDigits transcodes VIN digits to a numeric value func transcodeDigits(vin string) int { var digitSum = 0 var code int @@ -114,30 +114,8 @@ func transcodeDigits(vin string) int { return digitSum } -// isNilFixed . -// func isNil(i interface{}) bool { -// if i == nil { -// return true -// } -// switch reflect.TypeOf(i).Kind() { -// case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: -// return reflect.ValueOf(i).IsNil() -// } -// return false -// } - // timeTrack . // func timeTrack(name string) { // start := time.Now() // fmt.Printf("%s took %v\n", name, time.Since(start)) // } - -// contains . -// func contains(s []string, str string) bool { -// for _, v := range s { -// if v == str { -// return true -// } -// } -// return false -// } diff --git a/utils_test.go b/utils_test.go index d0a6812..acc98e5 100644 --- a/utils_test.go +++ b/utils_test.go @@ -7,6 +7,7 @@ import ( "time" ) +// timestamp returns the current time in milliseconds since epoch as a string. func TestTimestamp(t *testing.T) { ts1 := timestamp() time.Sleep(1 * time.Millisecond) @@ -22,6 +23,7 @@ func TestTimestamp(t *testing.T) { } } +// timestamp returns the current time in milliseconds since epoch as a string. func TestTimestamp_Format(t *testing.T) { ts := timestamp() matched, err := regexp.MatchString(`^\d+$`, ts) @@ -33,6 +35,7 @@ func TestTimestamp_Format(t *testing.T) { } } +// urlToGen replaces "api_gen" in the URL with the specified generation. func TestUrlToGen(t *testing.T) { tests := []struct { url, gen, want string @@ -51,6 +54,7 @@ func TestUrlToGen(t *testing.T) { } } +// vinCheck validates the VIN check digit and returns the corrected VIN. func TestVinCheck_Valid(t *testing.T) { // Example valid VIN: 1HGCM82633A004352 (check digit is '3') vin := "1HGCM82633A004352" @@ -63,6 +67,7 @@ func TestVinCheck_Valid(t *testing.T) { } } +// TestVinCheck_InvalidCheckDigit tests a VIN with an incorrect check digit. func TestVinCheck_InvalidCheckDigit(t *testing.T) { vin := "1HGCM82633A004352" // Change check digit (9th char) to '9' @@ -77,6 +82,7 @@ func TestVinCheck_InvalidCheckDigit(t *testing.T) { } } +// TestVinCheck_WrongLength tests a VIN that is not 17 characters long. func TestVinCheck_WrongLength(t *testing.T) { vin := "1234567890123456" // 16 chars valid, corrected := vinCheck(vin) @@ -88,6 +94,7 @@ func TestVinCheck_WrongLength(t *testing.T) { } } +// transcodeDigits computes the sum of the VIN digits according to the VIN rules. func TestTranscodeDigits(t *testing.T) { // Use a known VIN and manually compute the sum vin := "1HGCM82633A004352" @@ -99,6 +106,7 @@ func TestTranscodeDigits(t *testing.T) { } } +// TestVinCheck_XCheckDigit tests a VIN with 'X' as the check digit. func TestVinCheck_XCheckDigit(t *testing.T) { // VIN with check digit 'X' vin := "1M8GDM9AXKP042788" @@ -111,6 +119,7 @@ func TestVinCheck_XCheckDigit(t *testing.T) { } } +// TestUrlToGen_NoApiGen tests the case where the URL does not contain "api_gen". func TestUrlToGen_NoApiGen(t *testing.T) { url := "https://host/endpoint" gen := "g1" diff --git a/vehicle.go b/vehicle.go index 277579a..991da5d 100644 --- a/vehicle.go +++ b/vehicle.go @@ -36,7 +36,7 @@ import ( // }, // } -// Vehicle . +// Vehicle represents a Subaru vehicle with various attributes and methods to interact with it. type Vehicle struct { CarId int64 Vin string // SELECT CAR REQUEST > "vin": "4S4BTGND8L3137058" @@ -88,7 +88,7 @@ type Vehicle struct { } -// ClimateProfile . +// ClimateProfile represents a climate control profile for a Subaru vehicle. type ClimateProfile struct { Name string `json:"name,omitempty"` VehicleType string `json:"vehicleType,omitempty"` // vehicleType [ gas | phev ] @@ -107,7 +107,7 @@ type ClimateProfile struct { StartConfiguration string `json:"startConfiguration"` // startConfiguration [ START_ENGINE_ALLOW_KEY_IN_IGNITION (gas) | START_CLIMATE_CONTROL_ONLY_ALLOW_KEY_IN_IGNITION (phev) ] } -// GeoLocation . +// GeoLocation represents the geographical location of a Subaru vehicle. type GeoLocation struct { Latitude float64 // 40.700184 Longitude float64 // -74.401375 @@ -116,7 +116,7 @@ type GeoLocation struct { Updated time.Time } -// Door . +// Door represents a door of a Subaru vehicle with its position, sub-position, status, and lock state. type Door struct { Position string // front | rear | boot | enginehood SubPosition string // right | left @@ -125,7 +125,7 @@ type Door struct { Updated time.Time } -// Window . +// Window represents a window of a Subaru vehicle with its position, sub-position, status, and last updated time. type Window struct { Position string SubPosition string @@ -133,7 +133,7 @@ type Window struct { Updated time.Time } -// Tire . +// Tire represents a tire of a Subaru vehicle with its position, sub-position, pressure, pressure in PSI, and last updated time. type Tire struct { Position string SubPosition string @@ -143,7 +143,7 @@ type Tire struct { // Status string } -// Trouble . +// Trouble represents a trouble or issue with a Subaru vehicle, containing a description of the trouble. type Trouble struct { Description string } @@ -211,7 +211,7 @@ func (v *Vehicle) String() string { return vString } -// Lock . +// Lock // Sends a command to lock doors. func (v *Vehicle) Lock() (chan string, error) { if !v.getRemoteOptionsStatus() { @@ -235,7 +235,7 @@ func (v *Vehicle) Lock() (chan string, error) { return ch, nil } -// Unlock . +// Unlock // Send command to unlock doors. func (v *Vehicle) Unlock() (chan string, error) { if !v.getRemoteOptionsStatus() { @@ -259,7 +259,7 @@ func (v *Vehicle) Unlock() (chan string, error) { return ch, nil } -// EngineStart . +// EngineStart // Sends a command to start engine and set climate control. func (v *Vehicle) EngineStart() (chan string, error) { if !v.getRemoteOptionsStatus() { @@ -296,7 +296,7 @@ func (v *Vehicle) EngineStart() (chan string, error) { return ch, nil } -// EngineStop . +// EngineStop // Sends a command to stop engine. func (v *Vehicle) EngineStop() (chan string, error) { if !v.getRemoteOptionsStatus() { @@ -320,7 +320,7 @@ func (v *Vehicle) EngineStop() (chan string, error) { return ch, nil } -// LightsStart . +// LightsStart // Sends a command to flash lights. func (v *Vehicle) LightsStart() (chan string, error) { if !v.getRemoteOptionsStatus() { @@ -345,7 +345,7 @@ func (v *Vehicle) LightsStart() (chan string, error) { return ch, nil } -// LightsStop . +// LightsStop // Sends a command to stop flash lights. func (v *Vehicle) LightsStop() (chan string, error) { if !v.getRemoteOptionsStatus() { @@ -371,7 +371,7 @@ func (v *Vehicle) LightsStop() (chan string, error) { return ch, nil } -// HornStart . +// HornStart // Send command to sound horn. func (v *Vehicle) HornStart() (chan string, error) { if !v.getRemoteOptionsStatus() { @@ -397,7 +397,7 @@ func (v *Vehicle) HornStart() (chan string, error) { return ch, nil } -// HornStop . +// HornStop // Send command to sound horn. func (v *Vehicle) HornStop() (chan string, error) { if !v.getRemoteOptionsStatus() { @@ -423,7 +423,7 @@ func (v *Vehicle) HornStop() (chan string, error) { return ch, nil } -// ChargeStart . +// ChargeStart func (v *Vehicle) ChargeOn() (chan string, error) { if !v.getRemoteOptionsStatus() { v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"]) @@ -447,7 +447,7 @@ func (v *Vehicle) ChargeOn() (chan string, error) { } } -// GetLocation . +// GetLocation func (v *Vehicle) GetLocation(force bool) (chan string, error) { if !v.getRemoteOptionsStatus() { v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"]) @@ -580,7 +580,7 @@ func (v *Vehicle) GetClimateQuickPresets() error { return nil } -// GetClimateUserPresets . +// GetClimateUserPresets func (v *Vehicle) GetClimateUserPresets() error { if !v.getRemoteOptionsStatus() { v.client.logger.Error(APP_ERRORS["SUBSCRIBTION_REQUIRED"]) diff --git a/vehicle_test.go b/vehicle_test.go index 4bcd5a7..bc138a9 100644 --- a/vehicle_test.go +++ b/vehicle_test.go @@ -6,6 +6,7 @@ import ( "testing" ) +// TestGetClimateQuickPresets_Success tests the retrieval of quick climate presets. func TestGetClimatePresets_Success(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { // Handle API_LOGIN endpoint @@ -32,7 +33,7 @@ func TestGetClimatePresets_Success(t *testing.T) { ts.Start() defer ts.Close() - cfg := makeConfig(t) + cfg := mockConfig(t) msc, err := New(cfg) if err != nil { @@ -49,6 +50,7 @@ func TestGetClimatePresets_Success(t *testing.T) { } } +// TestGetClimateQuickPresets_Success tests the retrieval of quick climate presets. func TestGetClimateQuickPresets_Success(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { // Handle API_LOGIN endpoint @@ -75,7 +77,7 @@ func TestGetClimateQuickPresets_Success(t *testing.T) { ts.Start() defer ts.Close() - cfg := makeConfig(t) + cfg := mockConfig(t) msc, err := New(cfg) if err != nil { @@ -92,6 +94,7 @@ func TestGetClimateQuickPresets_Success(t *testing.T) { } } +// TestGetClimateUserPresets_Success tests the retrieval of user-defined climate presets. func TestGetClimateUserPresets_Success(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { // Handle API_LOGIN endpoint @@ -118,7 +121,7 @@ func TestGetClimateUserPresets_Success(t *testing.T) { ts.Start() defer ts.Close() - cfg := makeConfig(t) + cfg := mockConfig(t) msc, err := New(cfg) if err != nil { @@ -135,6 +138,7 @@ func TestGetClimateUserPresets_Success(t *testing.T) { } } +// TestGetVehicleCondition_Success tests the GetVehicleCondition method func TestGetVehicleStatus_Success(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { // Handle API_LOGIN endpoint @@ -161,7 +165,7 @@ func TestGetVehicleStatus_Success(t *testing.T) { ts.Start() defer ts.Close() - cfg := makeConfig(t) + cfg := mockConfig(t) msc, err := New(cfg) if err != nil { @@ -178,6 +182,7 @@ func TestGetVehicleStatus_Success(t *testing.T) { } } +// TestGetVehicleCondition_Success tests the GetVehicleCondition method func TestGetVehicleCondition_Success(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { // Handle API_LOGIN endpoint @@ -204,7 +209,7 @@ func TestGetVehicleCondition_Success(t *testing.T) { ts.Start() defer ts.Close() - cfg := makeConfig(t) + cfg := mockConfig(t) msc, err := New(cfg) if err != nil { @@ -221,6 +226,7 @@ func TestGetVehicleCondition_Success(t *testing.T) { } } +// TestGetVehicleHealth_Success tests the successful retrieval of vehicle health data. func TestGetVehicleHealth_Success(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { // Handle API_LOGIN endpoint @@ -247,7 +253,7 @@ func TestGetVehicleHealth_Success(t *testing.T) { ts.Start() defer ts.Close() - cfg := makeConfig(t) + cfg := mockConfig(t) msc, err := New(cfg) if err != nil {