Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing modules for class #1126

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
Binary file added .DS_Store
Binary file not shown.
2 changes: 1 addition & 1 deletion config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
TMDB_API_KEY = '<<TMDB_API_KEY>>'
WORDS_API_KEY = '<<WORDS_API_KEY>>'
YOUTUBE_DATA_API_KEY = '<<YOUTUBE_DATA_API_KEY>>'

BHT_THESAURUS_API_KEY = '6c30eb98d1ba58088d0fcdfc84874681'
# Local Testing
WIT_LOCAL_DATA = 'local/wit.json'

Expand Down
Binary file added modules/.DS_Store
Binary file not shown.
5 changes: 3 additions & 2 deletions modules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

import config
import modules
from src import *
from modules import src
#from src import *
from templates.quick_replies import add_quick_reply
from templates.text import TextTemplate

Expand Down Expand Up @@ -85,4 +86,4 @@ def search(input, sender=None, postback=False):
'I\'m sorry; I\'m not sure I understand what you\'re trying to say.\nTry typing "help" or "request"').get_message()
message = add_quick_reply(message, 'Help', modules.generate_postback('help'))
message = add_quick_reply(message, 'Request', modules.generate_postback('request'))
return message
return message
Binary file added modules/src/.DS_Store
Binary file not shown.
2 changes: 2 additions & 0 deletions modules/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
'fact',
'hello',
'help',
'horoscope',
'joke',
'lyrics',
'movie',
Expand All @@ -18,6 +19,7 @@
'ping',
'quote',
'request',
# 'thesaurus',
'thanks',
'time',
'url',
Expand Down
51 changes: 51 additions & 0 deletions modules/src/horoscope.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import requests
import requests_cache
from templates.text import TextTemplate

AZTRO_API_URL = "https://aztro.sameerkumar.website/"

def process(input, entities):
output = {}
try:
# Extract zodiac sign and day from entities
sign = entities.get('sign', [{}])[0].get('value', '').lower()
day = entities.get('day', [{}])[0].get('value', 'today').lower()

# Validate zodiac sign
valid_signs = [
"aries", "taurus", "gemini", "cancer", "leo", "virgo",
"libra", "scorpio", "sagittarius", "capricorn", "aquarius", "pisces"
]
if sign not in valid_signs:
raise ValueError("Invalid zodiac sign")

# Validate day
valid_days = ["today", "yesterday", "tomorrow"]
if day not in valid_days:
raise ValueError("Invalid day")

# Make API request
with requests_cache.enabled('horoscope_cache', backend='sqlite', expire_after=86400):
response = requests.post(AZTRO_API_URL, params={"sign": sign, "day": day})
data = response.json()

# Extract the horoscope from the response
horoscope = data.get('description', "I couldn't fetch the horoscope right now.")

# Create the output message
output['input'] = input
output['output'] = TextTemplate(f"Horoscope for {sign.capitalize()} ({day.capitalize()}):\n{horoscope}").get_message()
output['success'] = True

except Exception as e:
# Error handling and fallback messages
error_message = "I couldn't fetch your horoscope."
error_message += "\nPlease try again with something like:"
error_message += "\n - horoscope for leo"
error_message += "\n - today's cancer horoscope"
error_message += "\n - tomorrow's virgo horoscope"
output['error_msg'] = TextTemplate(error_message).get_message()
output['success'] = False

return output

32 changes: 32 additions & 0 deletions modules/src/horoscopeTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import requests

AZTRO_API_URL = "https://aztro.sameerkumar.website/"

def process(input, entities):
output = {}
try:
sign = ""
if "sign" in entities and len(entities["sign"]) > 0:
sign = entities["sign"][0]["value"].lower()
day = "today" # if you extract the specific day and value from the entities would make the code concise and readable
if "day" in entities and len(entities["day"]) > 0:
day = entities["day"][0]["value"].lower()
#Add checks for valid zodiac signs and days,
# the code needs to ensure the input aligns with lists of valid values for both days and signs
response = requests.post(AZTRO_API_URL, params={"sign": sign, "day": day})
if response.status_code != 200:
raise Exception("API request failed")

data = response.json()
horoscope = data["description"] if "description" in data else "Horoscope not available."
# needs to have handling for when api returns empty results
output["input"] = input
output["output"] = {"text": f"Horoscope for {sign.capitalize()} ({day.capitalize()}): {horoscope}"}
output["success"] = True

except Exception as e:
output["error_msg"] = {"text": "An error occurred. Try again later."}
output["success"] = False
#Error messages needs to be less vague for good practice
return output

43 changes: 43 additions & 0 deletions modules/src/thesaurus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import requests
import config
from templates.text import TextTemplate

API_URL = "https://words.bighugelabs.com/api/2"

def process(input_text, entities):
output = {}
try:
# Extract the word from the entities
word = entities.get("word", [{}])[0].get("value", "")
if not word.strip():
raise ValueError("Input cannot be empty. Please provide a valid word for thesaurus lookup.")

# Make the API request
response = requests.get(f"{API_URL}/{config.BHT_THESAURUS_API_KEY}/{word}/json")
if response.status_code != 200:
raise Exception(f"API request failed with status code {response.status_code}")

# Parse the JSON response
try:
data = response.json()
except ValueError:
raise ValueError("Malformed API response. Please try again later.")

# Extract synonyms from the response
synonyms = []
for key in ["noun", "verb", "adjective", "adverb"]:
if key in data and "syn" in data[key]:
synonyms.extend(data[key]["syn"])

if synonyms:
output_text = f"Synonyms for '{word}':\n" + ", ".join(synonyms)
output["output"] = TextTemplate(output_text).get_message()
output["success"] = True
else:
output["error_msg"] = TextTemplate(f"Couldn't find synonyms for '{word}'.").get_message()
output["success"] = False
except Exception as e:
output["error_msg"] = TextTemplate(f"An error occurred: {str(e)}").get_message()
output["success"] = False

return output
26 changes: 26 additions & 0 deletions modules/src/thesaurusTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import requests

API_URL = "https://words.bighugelabs.com/api/2"

def process(input_text, entities):
output = {}
try:
word = entities["word"][0]["value"]
api_key = "hardcoded_api_key"# Its best practice not to store api key directly in the code
# The code assumes that word is always present in entities missing input will cause a crash.

response = requests.get(f"{API_URL}/{api_key}/{word}/json")
data = response.json()

synonyms = data["noun"]["syn"]
#The api lets you do mort parts of speech than just a noun
output["output"] = f"Synonyms: {', '.join(synonyms)}"
output["success"] = True
# code assumes the API request will always succeed, but it may fail due to network issues, invalid API keys, or server errors.

except:
output["error_msg"] = "An error occurred."
output["success"] = False
# error reports are too vague and needs to be more in depth for best practice and makes error hndling simpler
return output

Binary file added modules/tests/.DS_Store
Binary file not shown.
143 changes: 143 additions & 0 deletions modules/tests/test_horoscope.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import sys
import os
import unittest

# Add the parent directory of src to sys.path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
from unittest.mock import patch # Import patch
from modules.src.horoscope import process
class TestHoroscope(unittest.TestCase):

def test_valid_input(self): #1
"""Test case with valid 'sign' and 'day' inputs"""
input_text = "horoscope for aries"
entities = {"sign": [{"value": "aries"}], "day": [{"value": "today"}]}
result = process(input_text, entities)
self.assertIn("output", result)
self.assertTrue(result["success"])

def test_missing_day(self): #2
"""Test case with 'sign' provided but missing 'day'"""
input_text = "horoscope for aries"
entities = {"sign": [{"value": "aries"}]}
result = process(input_text, entities)
self.assertIn("output", result)
self.assertTrue(result["success"])

def test_missing_sign(self):#3
"""Test case with 'day' provided but missing 'sign'"""
input_text = "horoscope for today"
entities = {"day": [{"value": "today"}]}
result = process(input_text, entities)
self.assertIn("error_msg", result)
self.assertFalse(result["success"])

def test_invalid_sign(self):#4
"""Test case with an invalid zodiac sign"""
input_text = "horoscope for dragon"
entities = {"sign": [{"value": "dragon"}], "day": [{"value": "today"}]}
result = process(input_text, entities)
self.assertIn("error_msg", result)
self.assertFalse(result["success"])

def test_invalid_day(self):#5
"""Test case with an invalid day"""
input_text = "horoscope for aries on someday"
entities = {"sign": [{"value": "aries"}], "day": [{"value": "someday"}]}
result = process(input_text, entities)
self.assertIn("error_msg", result)
self.assertFalse(result["success"])


def test_all_zodiac_signs(self):#7
"""Test with all valid zodiac signs"""
zodiac_signs = [
"aries", "taurus", "gemini", "cancer", "leo", "virgo",
"libra", "scorpio", "sagittarius", "capricorn", "aquarius", "pisces"
]
for sign in zodiac_signs:
with self.subTest(sign=sign):

input_text = f"horoscope for {sign}"
entities = {"sign": [{"value": sign}], "day": [{"value": "today"}]}
result = process(input_text, entities)
self.assertIn("output", result)
self.assertTrue(result["success"])
def test_all_days(self):#8
"""Test with all valid day inputs"""
days = ["yesterday", "today", "tomorrow"]
for day in days:
with self.subTest(day=day):
input_text = f"horoscope for aries on {day}"
entities = {"sign": [{"value": "aries"}], "day": [{"value": day}]}
result = process(input_text, entities)
self.assertIn("output", result)
self.assertTrue(result["success"])
def test_fuzzy_inputs(self): # Include this inside the TestHoroscope class
"""Test fuzzy inputs for zodiac signs"""
fuzzy_inputs = ["aeries", "taurrus", "jimini", "leo.", "cancer@", "sagitarius"]
for fuzzy_sign in fuzzy_inputs:
with self.subTest(fuzzy_sign=fuzzy_sign):
input_text = f"horoscope for {fuzzy_sign}"
entities = {"sign": [{"value": fuzzy_sign}], "day": [{"value": "today"}]}
result = process(input_text, entities)
self.assertIn("error_msg", result)
self.assertFalse(result["success"])

def test_empty_entities(self): # Include this inside the TestHoroscope class
"""Test behavior with empty entities"""
input_text = "horoscope"
entities = {}
result = process(input_text, entities)
self.assertIn("error_msg", result)
self.assertFalse(result["success"])

def test_completely_invalid_input(self):
"""Test with a completely invalid input"""
input_text = "random gibberish"
entities = {}
result = process(input_text, entities)
self.assertIn("error_msg", result)
self.assertFalse(result["success"])
def test_edge_case_inputs(self): # fails
"""Test edge cases for input text and entities"""
edge_cases = [
{"input_text": "", "entities": {"sign": [{"value": "aries"}], "day": [{"value": "today"}]}},
{"input_text": "123456", "entities": {"sign": [{"value": "123456"}], "day": [{"value": "today"}]}},
{"input_text": "!@#$%^&*", "entities": {"sign": [{"value": "!@#$%^&*"}], "day": [{"value": "today"}]}},
]
for case in edge_cases:
with self.subTest(input_text=case["input_text"]):
result = process(case["input_text"], case["entities"])
self.assertIn("error_msg", result)
self.assertFalse(result["success"])
@patch("modules.src.horoscope.requests.get")
def test_api_unavailability(self, mock_get):
"""Test behavior when the API is unavailable"""
mock_get.side_effect = requests.exceptions.ConnectionError
input_text = "horoscope for aries today"
entities = {"sign": [{"value": "aries"}], "day": [{"value": "today"}]}
result = process(input_text, entities)
self.assertIn("error_msg", result)
self.assertFalse(result["success"])
@patch("modules.src.horoscope.requests.get")
def test_api_timeout(self, mock_get):
"""Test behavior when the API times out"""
mock_get.side_effect = requests.exceptions.Timeout
input_text = "horoscope for leo today"
entities = {"sign": [{"value": "leo"}], "day": [{"value": "today"}]}
result = process(input_text, entities)
self.assertIn("error_msg", result)
self.assertFalse(result["success"])
@patch("modules.src.horoscope.requests.get")
def test_malformed_api_response(self, mock_get):
"""Test behavior with a malformed API response"""
mock_get.return_value.json.return_value = {"unexpected": "data"}
input_text = "horoscope for cancer today"
entities = {"sign": [{"value": "cancer"}], "day": [{"value": "today"}]}
result = process(input_text, entities)
self.assertIn("error_msg", result)
self.assertFalse(result["success"])

if __name__ == "__main__":
unittest.main()
Loading