Skip to content

Hanne - tests #12

Merged
merged 4 commits into from
Apr 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
103 changes: 103 additions & 0 deletions tests/unit/test_clean_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import unittest
import pandas as pd
import numpy as np
from src.my_package.util import (
kelvin_to_celsius,
ensure_rain_column,
ensure_snow_column,
fill_rain_column,
fill_snow_column,
extract_city_df
)

class TestUtilFunctions(unittest.TestCase):

# Test kelvin to celsius conversion
def test_kelvin_to_celsius(self):
kelvin_temp = 300
expected_celsius = 26.85

result = kelvin_to_celsius(kelvin_temp)

self.assertAlmostEqual(result, expected_celsius, places=2)

# Test if 'rain.1h' column is added when not present
def test_ensure_rain_column(self):
df = pd.DataFrame({
'temp': [300, 302, 305],
'humidity': [80, 82, 78]
})

df = ensure_rain_column(df)

# Check if the 'rain.1h' column is present after function call
self.assertTrue('rain.1h' in df.columns)
# Check if the column has NaN values
self.assertTrue(df['rain.1h'].isna().all())

# Test if 'snow.1h' column is added when not present
def test_ensure_snow_column(self):
df = pd.DataFrame({
'temp': [300, 302, 305],
'humidity': [80, 82, 78]
})

df = ensure_snow_column(df)

# Check if the 'snow.1h' column is present after function call
self.assertTrue('snow.1h' in df.columns)
# Check if the column has NaN values
self.assertTrue(df['snow.1h'].isna().all())

# Test if NaN values in 'rain.1h' are filled with 0
def test_fill_rain_column(self):
df = pd.DataFrame({
'temp': [300, 302, 305],
'rain.1h': [np.nan, 1.0, np.nan]
})

df = fill_rain_column(df)

# Check if NaN values are replaced with 0
self.assertEqual(df['rain.1h'].iloc[0], 0)
self.assertEqual(df['rain.1h'].iloc[2], 0)

# Test if NaN values in 'snow.1h' are filled with 0
def test_fill_snow_column(self):
df = pd.DataFrame({
'temp': [300, 302, 305],
'snow.1h': [np.nan, 0.5, np.nan]
})

df = fill_snow_column(df)

# Check if NaN values are replaced with 0
self.assertEqual(df['snow.1h'].iloc[0], 0)
self.assertEqual(df['snow.1h'].iloc[2], 0)

# Test extracting city DataFrame from JSON data
def test_extract_city_df(self):
weather_data = {
"list": [
{"dt": 1618245600, "temp": 290, "humidity": 85, "weather": [{"description": "clear sky"}]},
{"dt": 1618255600, "temp": 295, "humidity": 80, "weather": [{"description": "cloudy"}]},
{"dt": 1618265600, "temp": 300, "humidity": 75, "weather": [{"description": "sunny"}]}
]
}

df = extract_city_df(weather_data)

# Ensure that 'dt' is the index and in datetime format
self.assertTrue(pd.api.types.is_datetime64_any_dtype(df.index))

# Check if duplicates are removed based on 'dt'
self.assertEqual(len(df), 3)

# Check if 'weather' column was dropped
self.assertNotIn('weather', df.columns)

# Check the correct conversion of 'dt' to datetime
self.assertEqual(df.index[0], pd.to_datetime(1618245600, unit='s'))

if __name__ == '__main__':
unittest.main()
63 changes: 63 additions & 0 deletions tests/unit/test_format.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import unittest
import os

# Function to check if the .env file is created correctly
def check_env_file_creation():
env_filepath = os.path.join(os.path.dirname(__file__), "../../.env")

# Check if the .env file exists
if not os.path.exists(env_filepath):
return False, f".env file does not exist at {env_filepath}"

# Check if the file contains the expected keys
with open(env_filepath, 'r') as env_file:
file_content = env_file.read()

if 'API_EMAIL' not in file_content or 'API_KEY' not in file_content:
return False, "The .env file is missing 'API_EMAIL' or 'API_KEY'."

return True, ".env file is correctly created with API credentials."

# Function to check if the API_EMAIL and API_KEY values are formatted correctly
def check_api_credentials():
env_filepath = os.path.join(os.path.dirname(__file__), "../../.env")

with open(env_filepath, 'r') as env_file:
lines = env_file.readlines()

api_email = None
api_key = None

for line in lines:
if 'API_EMAIL' in line:
api_email = line.strip().split('=')[1].strip().replace('"', '')
elif 'API_KEY' in line:
api_key = line.strip().split('=')[1].strip().replace('"', '')

if not api_email or not api_key:
return False, "API_EMAIL or API_KEY is missing or formatted incorrectly."

# Validate the email format (basic validation for '@' symbol)
if '@' not in api_email:
return False, "API_EMAIL format is incorrect."

# Validate API_KEY length (assuming it's a standard length for OpenWeatherMap keys)
if len(api_key) < 32: # OpenWeatherMap keys are typically longer
return False, "API_KEY format is incorrect."

return True, "API credentials are valid and properly formatted."

class TestDataFormatConsistency(unittest.TestCase):

# Test if the .env file is created correctly
def test_env_file_creation(self):
valid, message = check_env_file_creation()
self.assertTrue(valid, message)

# Test if the API credentials are correct
def test_api_credentials(self):
valid, message = check_api_credentials()
self.assertTrue(valid, message)

if __name__ == '__main__':
unittest.main()
82 changes: 82 additions & 0 deletions tests/unit/test_format_yeardata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import unittest
import os
import requests
from unittest.mock import patch
from src.my_package.year_data import fetch_data

class TestDataFormatConsistency(unittest.TestCase):

@patch('requests.get')
def test_fetch_data_success(self, mock_get):
# Sample valid response data structure for the API
mock_data = {
'city': {
'name': 'Maura',
'country': 'NO'
},
'data': [
{'year': 2020, 'temperature': 12.5, 'precipitation': 100},
{'year': 2021, 'temperature': 13.0, 'precipitation': 120},
]
}

# Mock the API response
mock_response = unittest.mock.Mock()
mock_response.status_code = 200
mock_response.json.return_value = mock_data

mock_get.return_value = mock_response

city_name = 'Maura'
data, folder = fetch_data(city_name)

# Check if the data returned is a dictionary and contains the expected keys
self.assertIsInstance(data, dict)
self.assertIn('city', data)
self.assertIn('data', data)

# Check if the 'city' key contains the expected structure
self.assertIn('name', data['city'])
self.assertIn('country', data['city'])

# Check if the 'data' key contains a list of dictionaries with required fields
self.assertIsInstance(data['data'], list)
self.assertGreater(len(data['data']), 0) # Check that there is at least one year record

for record in data['data']:
self.assertIn('year', record)
self.assertIn('temperature', record)
self.assertIn('precipitation', record)
self.assertIsInstance(record['year'], int)
self.assertIsInstance(record['temperature'], (int, float))
self.assertIsInstance(record['precipitation'], (int, float))

# Test that the folder variable exists and is a valid path
self.assertEqual(folder, "../data/output_statistikk")

@patch('requests.get')
def test_fetch_data_failure(self, mock_get):
# Mock an unsuccessful response (non-200 status code)
mock_response = unittest.mock.Mock()
mock_response.status_code = 404
mock_response.json.return_value = {}

mock_get.return_value = mock_response

city_name = 'Maura'
data, folder = fetch_data(city_name)

# Ensure data is empty and folder is correct even on failure
self.assertEqual(data, {})
self.assertEqual(folder, "../data/output_statistikk")

def test_api_key_in_env(self):
# Check if the API_KEY is loaded from the environment
api_key = os.getenv("API_KEY")

self.assertIsNotNone(api_key, "API_KEY is not set in the environment.")
self.assertIsInstance(api_key, str)
self.assertGreater(len(api_key), 0, "API_KEY should not be an empty string.")

if __name__ == '__main__':
unittest.main()
51 changes: 51 additions & 0 deletions tests/unit/test_input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import unittest
from unittest.mock import patch
import datetime
import sys
import os
from src.my_package import date_to_unix # This is the module we are testing

# This will make the absolute path from the root of the project, and will therefore work every time
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../src")))


#the goal for this test is to simulate user input and check if the returned UNIX timestamps are correct, for this we use mock input

class TestDateToUnix(unittest.TestCase):

@patch('builtins.input', side_effect=["2024, 4, 13, 12, 30", "2024, 4, 14, 15, 45"])
def test_get_unix_timestamp(self, mock_input):
_ = mock_input

# Expected UNIX timestamps for the hardcoded inputs
expected_start = int(datetime.datetime(2024, 4, 13, 12, 30).timestamp())
expected_end = int(datetime.datetime(2024, 4, 14, 15, 45).timestamp())

# Call the function which uses input() — the @patch above mocks input to return predefined values
unix_start, unix_end = date_to_unix.get_unix_timestamp()

# Check that the returned timestamps match the expected values
self.assertEqual(unix_start, expected_start)
self.assertEqual(unix_end, expected_end)

# test the function to ensure it correctly convert timestamp back to datetime
def test_from_unix_timestamp(self):

# Create known datetime objects
start_dt = datetime.datetime(2024, 4, 13, 12, 30)
end_dt = datetime.datetime(2024, 4, 14, 15, 45)

# Convert these to UNIX timestamps
unix_start = int(start_dt.timestamp())
unix_end = int(end_dt.timestamp())

# Use the function to convert them back to datetime
start_from_unix, end_from_unix = date_to_unix.from_unix_timestamp(unix_start, unix_end)

# Assert that the conversion back to datetime matches the original values
self.assertEqual(start_from_unix, start_dt)
self.assertEqual(end_from_unix, end_dt)

# Run all tests in this file when the script is executed directly
if __name__ == '__main__':
unittest.main()