test: basic test for stock analytics report
diff --git a/erpnext/stock/report/stock_analytics/test_stock_analytics.py b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
index f6c98f9..d9f10e5 100644
--- a/erpnext/stock/report/stock_analytics/test_stock_analytics.py
+++ b/erpnext/stock/report/stock_analytics/test_stock_analytics.py
@@ -1,13 +1,59 @@
 import datetime
 
+import frappe
 from frappe import _dict
 from frappe.tests.utils import FrappeTestCase
+from frappe.utils.data import add_to_date, get_datetime, getdate, nowdate
 
 from erpnext.accounts.utils import get_fiscal_year
-from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges
+from erpnext.stock.doctype.item.test_item import make_item
+from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
+from erpnext.stock.report.stock_analytics.stock_analytics import execute, get_period_date_ranges
+
+
+def stock_analytics(filters):
+	col, data, *_ = execute(filters)
+	return col, data
 
 
 class TestStockAnalyticsReport(FrappeTestCase):
+	def setUp(self) -> None:
+		self.item = make_item().name
+		self.warehouse = "_Test Warehouse - _TC"
+
+	def assert_single_item_report(self, movement, expected_buckets):
+		self.generate_stock(movement)
+		filters = _dict(
+			range="Monthly",
+			from_date=movement[0][1].replace(day=1),
+			to_date=movement[-1][1].replace(day=28),
+			value_quantity="Quantity",
+			company="_Test Company",
+			item_code=self.item,
+		)
+
+		cols, data = stock_analytics(filters)
+
+		self.assertEqual(len(data), 1)
+		row = frappe._dict(data[0])
+		self.assertEqual(row.name, self.item)
+		self.compare_analytics_row(row, cols, expected_buckets)
+
+	def generate_stock(self, movement):
+		for qty, posting_date in movement:
+			args = {"item": self.item, "qty": abs(qty), "posting_date": posting_date}
+			args["to_warehouse" if qty > 0 else "from_warehouse"] = self.warehouse
+			make_stock_entry(**args)
+
+	def compare_analytics_row(self, report_row, columns, expected_buckets):
+		# last (N) cols will be monthly data
+		no_of_buckets = len(expected_buckets)
+		month_cols = [col["fieldname"] for col in columns[-no_of_buckets:]]
+
+		actual_buckets = [report_row.get(col) for col in month_cols]
+
+		self.assertEqual(actual_buckets, expected_buckets)
+
 	def test_get_period_date_ranges(self):
 
 		filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06")
@@ -33,3 +79,26 @@
 		]
 
 		self.assertEqual(ranges, expected_ranges)
+
+	def test_basic_report_functionality(self):
+		"""Stock analytics report generates balance "as of" periods based on
+		user defined ranges. Check that this behaviour is correct."""
+
+		# create stock movement in 3 months at 15th of month
+		today = getdate()
+		movement = [
+			(10, add_to_date(today, months=0).replace(day=15)),
+			(-5, add_to_date(today, months=1).replace(day=15)),
+			(10, add_to_date(today, months=2).replace(day=15)),
+		]
+		self.assert_single_item_report(movement, [10, 5, 15])
+
+	def test_empty_month_in_between(self):
+		today = getdate()
+		movement = [
+			(100, add_to_date(today, months=0).replace(day=15)),
+			(-50, add_to_date(today, months=1).replace(day=15)),
+			# Skip a month
+			(20, add_to_date(today, months=3).replace(day=15)),
+		]
+		self.assert_single_item_report(movement, [100, 50, 50, 70])