def setUp(self) -> None:
        self.dph = 2

        def file_like_object_callback(mode: str):
            return StringIO()

        self.db = Database(file_like_object_callback, self.dph, 3, 48, 30, 12, 3)
        self.datasrc = 'data1'
        self.db.add_data_src(self.datasrc)

        self.datasource = Archive(self.dph, 3, 48, 30, 12, 3)

        self.datasource.raw = [(1, datetime(2020, 10, 30, 12, 00)),
                               (2, datetime(2020, 10, 30, 12, 15)),
                               (3, datetime(2020, 10, 30, 12, 30)),
                               (4, datetime(2020, 10, 30, 12, 45)),

                               (5, datetime(2020, 10, 30, 13, 00)),
                               (6, datetime(2020, 10, 30, 13, 15)),
                               (7, datetime(2020, 10, 30, 13, 30)),
                               (8, datetime(2020, 10, 30, 13, 45)),
                               ]
        self.datasource.day = [(hour * 10, datetime(2020, 10, hour//24 + 1, hour % 24, 00))
                               for hour in range(48)]
        self.datasource.month = [(day * 10, datetime(2020, day//30 + 3, day % 30 + 1, 00, 00))
                                 for day in range(60)]
        self.datasource.year = [(month * 10, datetime(2020, month + 1, 1, 00, 00))
                                for month in range(12)]
        self.datasource.years = [(year * 10, datetime(year + 2020, 1, 1, 00, 00))
                                 for year in range(3)]

        self.db.datasources[self.datasrc] = self.datasource
    def test_change_not_existing_electric_meter(self):
        # Init
        meter_id = 10
        self.state_file = FileLikeMock(
            json.dumps({
                'next_id': 11,
                'electric_meters': []
            }))
        self.logic.state = State(self.state_file_callback)
        self.logic.state.next_id = meter_id + 1
        self.logic.state.electric_meters = {}
        self.logic.state.electric_meters_dict = {}

        self.db_file = FileLikeMock(value=json.dumps({
            'data_per_hour': self.data_per_hour,
            'keep_raw': self.keep_raw,
            'keep_day': self.keep_day,
            'keep_month': self.keep_month,
            'keep_year': self.keep_year,
            'keep_years': self.keep_years,
            'data_sources': []
        }))
        self.logic.database = Database.load_database(
            self.database_file_callback)

        # Test
        self.assertRaises(KeyError, self.logic.change_electric_meter, meter_id)
    def setUp(self) -> None:
        self.data_per_hour = 1
        self.keep_raw = 3
        self.keep_day = 48
        self.keep_month = 30
        self.keep_year = 12
        self.keep_years = 3

        self.plugin_type = 'plugin1'

        self.db_file = FileLikeMock()
        self.state_file = FileLikeMock()

        self.database: Database = Database.create_database(
            self.database_file_callback, self.data_per_hour, self.keep_raw,
            self.keep_day, self.keep_month, self.keep_year, self.keep_years)
        self.plugin_loader: PluginLoader = PluginLoader(
            {'ElectricMeterPlugins': {
                self.plugin_type: 'TestPlugin'
            }}, 'testplugins')

        self.state: State = State.get_state(self.state_file_callback,
                                            self.deserialize_electric_meter)

        self.logic = Logic(self.state, self.database, self.plugin_loader,
                           self.data_per_hour)
    def setUp(self) -> None:

        self.database_file = FileLikeMock()

        def file_like_object_callback(mode: str):
            if mode == 'w':
                self.database_file.clear()
            return self.database_file

        self.file_like_object_callback = file_like_object_callback

        self.dph = 2
        self.keep_raw = 3
        self.keep_day = 48
        self.keep_month = 30
        self.keep_year = 12
        self.keep_years = 3

        self.db = Database(file_like_object_callback, self.dph, self.keep_raw, self.keep_day,
                           self.keep_month, self.keep_year, self.keep_years)
        self.db.add_data_src('data1')
        self.db.sync_file = False
 def test_save_load_database(self):
     # Create database and save
     db = Database.create_database(self.file_like_object_callback, self.dph, 3, 48, 30, 12, 3)
     db.sync_file = True
     db.add_data_src('test')
     db.add_data_src('data1')
     self.database_file.clear()
     db.add_data(123, 'data1')
     # Load data
     self.database_file.seek(0)  # Set pointer to file start
     db = Database.load_database(self.file_like_object_callback)
     # Assert
     self.assertIn('data1', db.datasources.keys())
     self.assertIn('test', db.datasources.keys())
     self.assertEqual(self.dph, db.dph)
     self.assertEqual(3, db.keep_raw)
     self.assertEqual(48, db.keep_day)
     self.assertEqual(30, db.keep_month)
     self.assertEqual(12, db.keep_year)
     self.assertEqual(3, db.keep_years)
     self.assertIn(123, [items['value'] for items in db.get_raw()['data1']])
     self.assertNotEqual(0, len([timestamp for value, timestamp in db.get_raw()['data1']]))
     # TODO vllt. bessere/schönere Art and timestamp zu kommen
     self.assertEqual(type(datetime.now()), type([item['timestamp'] for item in db.get_raw()['data1']][0]))
    def test_save_database(self):
        # Create database and save
        db = Database(self.file_like_object_callback, self.dph, self.keep_raw, self.keep_day,
                      self.keep_month, self.keep_year, self.keep_years)
        db.sync_file = True
        db.add_data_src('test')
        db.add_data_src('data1')
        self.database_file.truncate(0)  # Clear file
        self.database_file.seek(0)  # Set pointer to file start
        db.add_data(123, 'data1')

        self.database_file.seek(0)
        json_file = json.load(self.database_file)

        # Assert
        self.assertTrue(self.database_file.close_called)
        self.assertEqual(self.dph, json_file['data_per_hour'])
        self.assertEqual(self.keep_raw, json_file['keep_raw'])
        self.assertEqual(self.keep_day, json_file['keep_day'])
        self.assertEqual(self.keep_month, json_file['keep_month'])
        self.assertEqual(self.keep_year, json_file['keep_year'])
        self.assertEqual(self.keep_years, json_file['keep_years'])
    def test_load_save_database(self):
        self.db.sync_file = True
        database_dict = {
            'data_per_hour': self.dph,
            'keep_raw': self.keep_raw,
            'keep_day': self.keep_day,
            'keep_month': self.keep_month,
            'keep_year': self.keep_year,
            'keep_years': self.keep_years,
            'data_sources': [
                {
                    'id': 1,
                    'data': {
                        'day_counter': 0,
                        'month_counter': 0,
                        'year_counter': 0,
                        'years_counter': 0,
                        'day_sum': 0,
                        'month_sum': 0,
                        'year_sum': 0,
                        'years_sum': 0,
                        'raw_data': [],
                        'day_data': [],
                        'month_data': [],
                        'year_data': [],
                        'years_data': []
                    }
                }
            ]
        }
        self.database_file = FileLikeMock(json.dumps(database_dict))

        self.db = Database.load_database(self.file_like_object_callback)

        self.db._save_database()
        self.assertEqual(database_dict, json.loads(self.database_file.getvalue()))
    def test_load_database(self):
        def file_like_object_callback(mode: str):
            return StringIO('''{
            "data_per_hour": 2, 
            "keep_raw": 3, 
            "keep_day": 48, 
            "keep_month": 30, 
            "keep_year": 12, 
            "keep_years": 3, 
            "data_sources": [
                {"id": "test", 
                "data": {
                    "day_counter": 0,
                    "month_counter": 0, 
                    "year_counter": 0, 
                    "years_counter": 0, 
                    "day_sum": 0, 
                    "month_sum": 0, 
                    "year_sum": 0, 
                    "years_sum": 0, 
                    "raw_data": [], 
                    "day_data": [], 
                    "month_data": [], 
                    "year_data": [], 
                    "years_data": []
                    }
                }, 
                {"id": "data1", 
                "data": {
                    "day_counter": 1, 
                    "month_counter": 1, 
                    "year_counter": 1, 
                    "years_counter": 1, 
                    "day_sum": 123, 
                    "month_sum": 123, 
                    "year_sum": 123, 
                    "years_sum": 123, 
                    "raw_data": [[123, "2021-10-14T15:31:56.453965"]], 
                    "day_data": [], 
                    "month_data": [], 
                    "year_data": [], 
                    "years_data": []
                    }
                }
            ]}''')

        # Load data
        db = Database.load_database(file_like_object_callback)
        # Assert
        self.assertIn('data1', db.datasources.keys())
        self.assertIn('test', db.datasources.keys())
        self.assertEqual(self.dph, db.dph)
        self.assertEqual(3, db.keep_raw)
        self.assertEqual(48, db.keep_day)
        self.assertEqual(30, db.keep_month)
        self.assertEqual(12, db.keep_year)
        self.assertEqual(3, db.keep_years)
        self.assertIn(123, [items['value'] for items in db.get_raw()['data1']])
        self.assertNotEqual(0, len([timestamp for value, timestamp in db.get_raw()['data1']]))
        # TODO vllt. bessere/schönere Art and timestamp zu kommen
        self.assertEqual([datetime.fromisoformat("2021-10-14T15:31:56.453965")],
                         [item['timestamp'] for item in db.get_raw()['data1']])
    def test_create_database(self):
        db = Database(self.file_like_object_callback, self.dph, self.keep_raw, self.keep_day,
                      self.keep_month, self.keep_year, self.keep_years)

        self.assertIsNotNone(db)
        self.assertTrue(self.database_file.close_called)
class GetDataDatabaseTest(unittest.TestCase):

    def setUp(self) -> None:
        self.dph = 2

        def file_like_object_callback(mode: str):
            return StringIO()

        self.db = Database(file_like_object_callback, self.dph, 3, 48, 30, 12, 3)
        self.datasrc = 'data1'
        self.db.add_data_src(self.datasrc)

        self.datasource = Archive(self.dph, 3, 48, 30, 12, 3)

        self.datasource.raw = [(1, datetime(2020, 10, 30, 12, 00)),
                               (2, datetime(2020, 10, 30, 12, 15)),
                               (3, datetime(2020, 10, 30, 12, 30)),
                               (4, datetime(2020, 10, 30, 12, 45)),

                               (5, datetime(2020, 10, 30, 13, 00)),
                               (6, datetime(2020, 10, 30, 13, 15)),
                               (7, datetime(2020, 10, 30, 13, 30)),
                               (8, datetime(2020, 10, 30, 13, 45)),
                               ]
        self.datasource.day = [(hour * 10, datetime(2020, 10, hour//24 + 1, hour % 24, 00))
                               for hour in range(48)]
        self.datasource.month = [(day * 10, datetime(2020, day//30 + 3, day % 30 + 1, 00, 00))
                                 for day in range(60)]
        self.datasource.year = [(month * 10, datetime(2020, month + 1, 1, 00, 00))
                                for month in range(12)]
        self.datasource.years = [(year * 10, datetime(year + 2020, 1, 1, 00, 00))
                                 for year in range(3)]

        self.db.datasources[self.datasrc] = self.datasource

    def test_get_raw_since(self):
        # Test
        data = self.db.get_raw(since=datetime(2020, 10, 30, 13, 00))[self.datasrc]

        # Assert
        self.assertEqual(4, len(data))
        pairs = zip([self.datasource.raw[i] for i in range(4, 8)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_raw_exceed_since(self):
        # Test
        data = self.db.get_raw(since=datetime(2015, 10, 30, 12, 00))[self.datasrc]

        # Assert
        self.assertEqual(8, len(data))
        pairs = zip([self.datasource.raw[i] for i in range(0, 8)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_raw_until(self):
        # Test
        data = self.db.get_raw(until=datetime(2020, 10, 30, 13, 00))[self.datasrc]

        # Assert
        self.assertEqual(5, len(data))
        pairs = zip([self.datasource.raw[i] for i in range(0, 5)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_raw_exceed_until(self):
        # Test
        data = self.db.get_raw(until=datetime(2025, 10, 30, 12, 00))[self.datasrc]

        # Assert
        self.assertEqual(8, len(data))
        pairs = zip([self.datasource.raw[i] for i in range(0, 8)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_raw_until_since(self):
        # Test
        data = self.db.get_raw(since=datetime(2020, 10, 30, 12, 30),
                               until=datetime(2020, 10, 30, 13, 15))[self.datasrc]

        # Assert
        self.assertEqual(4, len(data))
        pairs = zip([self.datasource.raw[i] for i in range(2, 6)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_raw_exceed_until_since(self):
        # Test
        data = self.db.get_raw(since=datetime(2015, 10, 30, 12, 00),
                               until=datetime(2025, 10, 30, 12, 00))[self.datasrc]

        # Assert
        self.assertEqual(8, len(data))
        pairs = zip([self.datasource.raw[i] for i in range(0, 8)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_raw_invalid_until_since(self):
        # Since datetime is after until date time
        self.assertRaises(ValueError, self.db.get_raw,
                          until=datetime(2020, 10, 30, 12, 15), since=datetime(2020, 10, 30, 13, 30))

    def test_get_day_since(self):
        data = self.db.get_day(since=datetime(2020, 10, 1, 15, 00))[self.datasrc]

        # Assert
        self.assertEqual(34, len(data))
        pairs = zip([self.datasource.day[i] for i in range(15, 48)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_day_exceed_since(self):
        # Test
        data = self.db.get_day(since=datetime(2015, 10, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(49, len(data))
        pairs = zip([self.datasource.day[i] for i in range(0, 48)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_day_until(self):
        data = self.db.get_day(until=datetime(2020, 10, 2, 12, 00))[self.datasrc]

        # Assert
        self.assertEqual(37, len(data))
        pairs = zip([self.datasource.day[i] for i in range(0, 37)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_day_exceed_until(self):
        # Test
        data = self.db.get_day(until=datetime(2025, 10, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(49, len(data))
        pairs = zip([self.datasource.day[i] for i in range(0, 48)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_day_until_since(self):
        data = self.db.get_day(since=datetime(2020, 10, 1, 12, 00), until=datetime(2020, 10, 2, 12, 00))[self.datasrc]

        # Assert
        self.assertEqual(25, len(data))
        pairs = zip([self.datasource.day[i] for i in range(12, 36)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_day_exceed_until_since(self):
        # Test
        data = self.db.get_day(since=datetime(2015, 10, 1, 00, 00),
                               until=datetime(2025, 10, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(49, len(data))
        pairs = zip([self.datasource.day[i] for i in range(0, 48)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_day_invalid_until_since(self):
        # Since datetime is after until date time
        self.assertRaises(ValueError, self.db.get_day,
                          until=datetime(2020, 10, 1, 12, 00), since=datetime(2020, 10, 2, 12, 00))

    def test_get_month_since(self):
        data = self.db.get_month(since=datetime(2020, 3, 15, 0, 00))[self.datasrc]

        # Assert
        self.assertEqual(47, len(data))
        pairs = zip([self.datasource.month[i] for i in range(14, 60)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_month_exceed_since(self):
        # Test
        data = self.db.get_month(since=datetime(2015, 10, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(61, len(data))
        pairs = zip([self.datasource.month[i] for i in range(0, 60)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_month_until(self):
        data = self.db.get_month(until=datetime(2020, 4, 15, 0, 00))[self.datasrc]

        # Assert
        self.assertEqual(45, len(data))
        pairs = zip([self.datasource.month[i] for i in range(0, 45)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_month_exceed_until(self):
        # Test
        data = self.db.get_month(until=datetime(2025, 10, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(61, len(data))
        pairs = zip([self.datasource.month[i] for i in range(0, 60)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_month_until_since(self):
        data = self.db.get_month(since=datetime(2020, 3, 15, 0, 00),
                               until=datetime(2020, 4, 15, 0, 00))[self.datasrc]

        # Assert
        self.assertEqual(31, len(data))
        pairs = zip([self.datasource.month[i] for i in range(14, 45)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_month_exceed_until_since(self):
        # Test
        data = self.db.get_month(since=datetime(2015, 10, 1, 00, 00),
                                 until=datetime(2025, 10, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(61, len(data))
        pairs = zip([self.datasource.month[i] for i in range(0, 60)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_month_invalid_until_since(self):
        self.assertRaises(ValueError, self.db.get_month,
                          until=datetime(2020, 3, 15, 0, 00), since=datetime(2020, 4, 15, 0, 00))

    def test_get_year_since(self):
        data = self.db.get_year(since=datetime(2020, 6, 1, 0, 00))[self.datasrc]

        # Assert
        self.assertEqual(8, len(data))
        pairs = zip([self.datasource.year[i] for i in range(5, 12)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_year_exceed_since(self):
        # Test
        data = self.db.get_year(since=datetime(2015, 10, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(13, len(data))
        pairs = zip([self.datasource.year[i] for i in range(0, 12)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_year_until(self):
        data = self.db.get_year(until=datetime(2020, 6, 1, 0, 00))[self.datasrc]

        # Assert
        self.assertEqual(6, len(data))
        pairs = zip([self.datasource.year[i] for i in range(0, 6)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_year_exceed_until(self):
        # Test
        data = self.db.get_year(until=datetime(2025, 10, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(13, len(data))
        pairs = zip([self.datasource.year[i] for i in range(0, 12)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_year_until_since(self):
        data = self.db.get_year(since=datetime(2020, 3, 1, 0, 00),
                                until=datetime(2020, 9, 1, 0, 00))[self.datasrc]

        # Assert
        self.assertEqual(7, len(data))
        pairs = zip([self.datasource.year[i] for i in range(2, 9)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_year_exceed_until_since(self):
        # Test
        data = self.db.get_year(since=datetime(2015, 10, 1, 00, 00),
                                until=datetime(2025, 10, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(13, len(data))
        pairs = zip([self.datasource.year[i] for i in range(0, 12)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_year_invalid_until_since(self):
        self.assertRaises(ValueError, self.db.get_year,
                          until=datetime(2020, 3, 1, 0, 00), since=datetime(2020, 9, 1, 0, 00))

    def test_get_years_since(self):
        data = self.db.get_years(since=datetime(2021, 1, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(3, len(data))
        pairs = zip([self.datasource.years[i] for i in range(1, 3)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_years_exceed_since(self):
        # Test
        data = self.db.get_years(since=datetime(2015, 10, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(4, len(data))
        pairs = zip([self.datasource.years[i] for i in range(0, 3)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_years_until(self):
        data = self.db.get_years(until=datetime(2021, 1, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(2, len(data))
        pairs = zip([self.datasource.years[i] for i in range(0, 2)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_years_exceed_until(self):
        # Test
        data = self.db.get_years(until=datetime(2025, 10, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(4, len(data))
        pairs = zip([self.datasource.years[i] for i in range(0, 3)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_years_until_since(self):
        data = self.db.get_years(since=datetime(2021, 1, 1, 00, 00), until=datetime(2021, 1, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(1, len(data))
        pairs = zip([self.datasource.years[i] for i in range(1, 2)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_years_exceed_until_since(self):
        # Test
        data = self.db.get_years(since=datetime(2015, 10, 1, 00, 00),
                                 until=datetime(2025, 10, 1, 00, 00))[self.datasrc]

        # Assert
        self.assertEqual(4, len(data))
        pairs = zip([self.datasource.years[i] for i in range(0, 3)],
                    [(data['value'], data['timestamp']) for data in data])
        for expected, actual in pairs:
            self.assertEqual(expected, actual)

    def test_get_years_invalid_until_since(self):
        self.assertRaises(ValueError, self.db.get_month,
                          until=datetime(1, 1, 1, 0, 00), since=datetime(2, 1, 1, 0, 0))
class DatabaseTest(unittest.TestCase):

    def setUp(self) -> None:

        self.database_file = FileLikeMock()

        def file_like_object_callback(mode: str):
            if mode == 'w':
                self.database_file.clear()
            return self.database_file

        self.file_like_object_callback = file_like_object_callback

        self.dph = 2
        self.keep_raw = 3
        self.keep_day = 48
        self.keep_month = 30
        self.keep_year = 12
        self.keep_years = 3

        self.db = Database(file_like_object_callback, self.dph, self.keep_raw, self.keep_day,
                           self.keep_month, self.keep_year, self.keep_years)
        self.db.add_data_src('data1')
        self.db.sync_file = False

    def test_create_database(self):
        db = Database(self.file_like_object_callback, self.dph, self.keep_raw, self.keep_day,
                      self.keep_month, self.keep_year, self.keep_years)

        self.assertIsNotNone(db)
        self.assertTrue(self.database_file.close_called)
        # TODO test data

    def test_save_database(self):
        # Create database and save
        db = Database(self.file_like_object_callback, self.dph, self.keep_raw, self.keep_day,
                      self.keep_month, self.keep_year, self.keep_years)
        db.sync_file = True
        db.add_data_src('test')
        db.add_data_src('data1')
        self.database_file.truncate(0)  # Clear file
        self.database_file.seek(0)  # Set pointer to file start
        db.add_data(123, 'data1')

        self.database_file.seek(0)
        json_file = json.load(self.database_file)

        # Assert
        self.assertTrue(self.database_file.close_called)
        self.assertEqual(self.dph, json_file['data_per_hour'])
        self.assertEqual(self.keep_raw, json_file['keep_raw'])
        self.assertEqual(self.keep_day, json_file['keep_day'])
        self.assertEqual(self.keep_month, json_file['keep_month'])
        self.assertEqual(self.keep_year, json_file['keep_year'])
        self.assertEqual(self.keep_years, json_file['keep_years'])
        # TODO archive? überprüfen

    def test_load_database(self):
        def file_like_object_callback(mode: str):
            return StringIO('''{
            "data_per_hour": 2, 
            "keep_raw": 3, 
            "keep_day": 48, 
            "keep_month": 30, 
            "keep_year": 12, 
            "keep_years": 3, 
            "data_sources": [
                {"id": "test", 
                "data": {
                    "day_counter": 0,
                    "month_counter": 0, 
                    "year_counter": 0, 
                    "years_counter": 0, 
                    "day_sum": 0, 
                    "month_sum": 0, 
                    "year_sum": 0, 
                    "years_sum": 0, 
                    "raw_data": [], 
                    "day_data": [], 
                    "month_data": [], 
                    "year_data": [], 
                    "years_data": []
                    }
                }, 
                {"id": "data1", 
                "data": {
                    "day_counter": 1, 
                    "month_counter": 1, 
                    "year_counter": 1, 
                    "years_counter": 1, 
                    "day_sum": 123, 
                    "month_sum": 123, 
                    "year_sum": 123, 
                    "years_sum": 123, 
                    "raw_data": [[123, "2021-10-14T15:31:56.453965"]], 
                    "day_data": [], 
                    "month_data": [], 
                    "year_data": [], 
                    "years_data": []
                    }
                }
            ]}''')

        # Load data
        db = Database.load_database(file_like_object_callback)
        # Assert
        self.assertIn('data1', db.datasources.keys())
        self.assertIn('test', db.datasources.keys())
        self.assertEqual(self.dph, db.dph)
        self.assertEqual(3, db.keep_raw)
        self.assertEqual(48, db.keep_day)
        self.assertEqual(30, db.keep_month)
        self.assertEqual(12, db.keep_year)
        self.assertEqual(3, db.keep_years)
        self.assertIn(123, [items['value'] for items in db.get_raw()['data1']])
        self.assertNotEqual(0, len([timestamp for value, timestamp in db.get_raw()['data1']]))
        # TODO vllt. bessere/schönere Art and timestamp zu kommen
        self.assertEqual([datetime.fromisoformat("2021-10-14T15:31:56.453965")],
                         [item['timestamp'] for item in db.get_raw()['data1']])

    def test_save_load_database(self):
        # Create database and save
        db = Database.create_database(self.file_like_object_callback, self.dph, 3, 48, 30, 12, 3)
        db.sync_file = True
        db.add_data_src('test')
        db.add_data_src('data1')
        self.database_file.clear()
        db.add_data(123, 'data1')
        # Load data
        self.database_file.seek(0)  # Set pointer to file start
        db = Database.load_database(self.file_like_object_callback)
        # Assert
        self.assertIn('data1', db.datasources.keys())
        self.assertIn('test', db.datasources.keys())
        self.assertEqual(self.dph, db.dph)
        self.assertEqual(3, db.keep_raw)
        self.assertEqual(48, db.keep_day)
        self.assertEqual(30, db.keep_month)
        self.assertEqual(12, db.keep_year)
        self.assertEqual(3, db.keep_years)
        self.assertIn(123, [items['value'] for items in db.get_raw()['data1']])
        self.assertNotEqual(0, len([timestamp for value, timestamp in db.get_raw()['data1']]))
        # TODO vllt. bessere/schönere Art and timestamp zu kommen
        self.assertEqual(type(datetime.now()), type([item['timestamp'] for item in db.get_raw()['data1']][0]))

    def test_load_save_database(self):
        self.db.sync_file = True
        database_dict = {
            'data_per_hour': self.dph,
            'keep_raw': self.keep_raw,
            'keep_day': self.keep_day,
            'keep_month': self.keep_month,
            'keep_year': self.keep_year,
            'keep_years': self.keep_years,
            'data_sources': [
                {
                    'id': 1,
                    'data': {
                        'day_counter': 0,
                        'month_counter': 0,
                        'year_counter': 0,
                        'years_counter': 0,
                        'day_sum': 0,
                        'month_sum': 0,
                        'year_sum': 0,
                        'years_sum': 0,
                        'raw_data': [],
                        'day_data': [],
                        'month_data': [],
                        'year_data': [],
                        'years_data': []
                    }
                }
            ]
        }
        self.database_file = FileLikeMock(json.dumps(database_dict))

        self.db = Database.load_database(self.file_like_object_callback)

        self.db._save_database()
        self.assertEqual(database_dict, json.loads(self.database_file.getvalue()))

    def test_add_data_raw(self):

        for i in range(5):
            self.db.add_data(i, 'data1')

        data = self.db.get_raw()
        self.assertEqual(3, len(data['data1']))
        for i in range(2, 5):
            self.assertIn(i, [items['value'] for items in data['data1']])

    def test_add_data_day(self):
        for i in range((48 + 1) * self.dph):
            self.db.add_data(i, 'data1')

        data = self.db.get_day()

        self.assertEqual(48 + 1, len(data['data1']))
        for i in range(1, 48 + 1):
            self.assertIn(i * self.dph + (i * self.dph + 1), [items['value'] for items in data['data1']])

    def test_add_data_month(self):
        for i in range((30 + 1) * 24 * self.dph):
            self.db.add_data(1/self.dph, 'data1')

        data = self.db.get_month()

        self.assertEqual(30 + 1, len(data['data1']))
        for i in range(0, 30):
            self.assertEqual(24, [items['value'] for items in data['data1']][i])

    def test_add_data_year(self):
        for i in range((365 + 1) * 24 * self.dph):
            self.db.add_data(1/self.dph, 'data1')

        data = self.db.get_year()

        self.assertEqual(12 + 1, len(data['data1']))
        for i in range(12):
            self.assertEqual(720, [items['value'] for items in data['data1']][i])

    def test_add_data_years(self):
        for i in range(3 * 365 * 24 * self.dph):
            self.db.add_data(1/self.dph, 'data1')

        data = self.db.get_years()

        self.assertEqual(3 + 1, len(data['data1']))
        for i in range(3):
            self.assertEqual(8760, [items['value'] for items in data['data1']][i])

    def test_remove_datasource(self):
        self.assertIn('data1', self.db.get_raw().keys())
        self.db.remove_data_src('data1')
        self.assertNotIn('data1', self.db.get_raw().keys())
    def test_change_invalid_custom_change_electric_meter(self):
        # Init
        meter_id = 10
        name = 'Electric Meter not to be changed'
        custom_value = 11111
        custom = {'num': custom_value}
        serialized_electric_meter = {
            'id': meter_id,
            'name': name,
            'type': self.plugin_type,
            'custom': {
                'custom_data': custom_value
            }
        }
        state_file_dict = {
            'next_id': 11,
            'electric_meters': [serialized_electric_meter]
        }
        self.state_file = FileLikeMock(json.dumps(state_file_dict))
        self.logic.state = State(self.state_file_callback)
        self.logic.state.next_id = meter_id + 1
        electric_meter = self.deserialize_electric_meter(
            serialized_electric_meter)
        self.logic.state.electric_meters = {meter_id: electric_meter}
        self.logic.state.electric_meters_dict = {
            meter_id: serialized_electric_meter
        }

        database_dict = {
            'data_per_hour':
            self.data_per_hour,
            'keep_raw':
            self.keep_raw,
            'keep_day':
            self.keep_day,
            'keep_month':
            self.keep_month,
            'keep_year':
            self.keep_year,
            'keep_years':
            self.keep_years,
            'data_sources': [{
                'id': meter_id,
                'data': {
                    'day_counter': 0,
                    'month_counter': 0,
                    'year_counter': 0,
                    'years_counter': 0,
                    'day_sum': 0,
                    'month_sum': 0,
                    'year_sum': 0,
                    'years_sum': 0,
                    'raw_data': [],
                    'day_data': [],
                    'month_data': [],
                    'year_data': [],
                    'years_data': []
                }
            }]
        }
        self.db_file = FileLikeMock(json.dumps(database_dict))
        self.logic.database = Database.load_database(
            self.database_file_callback)

        # Test
        self.assertRaises(InvalidElectricMeterChangeException,
                          self.logic.change_electric_meter,
                          meter_id,
                          name=name,
                          custom={'num': -1})

        # Assert
        # Assert state file
        self.logic.state.save_state(
        )  # Trigger save of state to ensure changes are flushed to state_file
        self.assertEqual(state_file_dict,
                         json.loads(self.state_file.getvalue()))
        # Assert Electric Meter equality
        self.assertEqual(electric_meter.meter_type, self.plugin_type)
        self.assertEqual(electric_meter.name, name)
        self.assertEqual(electric_meter.custom_data, custom_value)
        # Assert Database
        self.logic.database._save_database()
        self.assertEqual(database_dict, json.loads(self.db_file.getvalue()))
    def test_change_electric_meter(self):
        # Init
        meter_id = 10
        changed_name = 'Changed name'
        changed_custom_value = 123
        serialized_electric_meter = {
            'id': meter_id,
            'name': 'Electric Meter to be deleted',
            'type': self.plugin_type,
            'custom': {
                'custom_data': 11111
            }
        }
        self.state_file = FileLikeMock(
            json.dumps({
                'next_id': 11,
                'electric_meters': [serialized_electric_meter]
            }))
        self.logic.state = State(self.state_file_callback)
        self.logic.state.next_id = meter_id + 1
        electric_meter = self.deserialize_electric_meter(
            serialized_electric_meter)
        electric_meter.set_serialize_hook(self.logic._call_serialize_hook)
        self.logic.state.electric_meters = {meter_id: electric_meter}
        self.logic.state.electric_meters_dict = {
            meter_id: serialized_electric_meter
        }

        database_dict = {
            'data_per_hour':
            self.data_per_hour,
            'keep_raw':
            self.keep_raw,
            'keep_day':
            self.keep_day,
            'keep_month':
            self.keep_month,
            'keep_year':
            self.keep_year,
            'keep_years':
            self.keep_years,
            'data_sources': [{
                'id': meter_id,
                'data': {
                    'day_counter': 0,
                    'month_counter': 0,
                    'year_counter': 0,
                    'years_counter': 0,
                    'day_sum': 0,
                    'month_sum': 0,
                    'year_sum': 0,
                    'years_sum': 0,
                    'raw_data': [],
                    'day_data': [],
                    'month_data': [],
                    'year_data': [],
                    'years_data': []
                }
            }]
        }
        self.db_file = FileLikeMock(value=json.dumps(database_dict))
        self.logic.database = Database.load_database(
            self.database_file_callback)

        # Test
        changed = self.logic.change_electric_meter(
            meter_id, name=changed_name, custom={'num': changed_custom_value})

        # Assert
        self.assertTrue(changed)
        # Assert state file
        self.logic.state.save_state(
        )  # Trigger save of state to ensure changes are flushed to state_file
        self.assertEqual(
            {
                'next_id':
                11,
                'electric_meters': [{
                    'id': meter_id,
                    'name': 'Changed name',
                    'type': self.plugin_type,
                    'custom': {
                        'custom_data': 123
                    }
                }]
            }, json.loads(self.state_file.getvalue()))
        # Assert Electric Meter changed
        self.assertEqual(electric_meter.meter_type, self.plugin_type)
        self.assertEqual(electric_meter.name, changed_name)
        self.assertEqual(electric_meter.custom_data, changed_custom_value)
        # Assert Database not changed
        self.logic.database._save_database()
        self.assertEqual(database_dict, json.loads(self.db_file.getvalue()))
    def test_remove_electric_meter(self):
        # Init
        meter_id = 10
        serialized_electric_meter = {
            'id': meter_id,
            'name': 'Electric Meter to be deleted',
            'type': self.plugin_type,
            'custom': {
                'custom_data': 11111
            }
        }
        self.state_file = FileLikeMock(
            json.dumps({
                'next_id': 11,
                'electric_meters': [serialized_electric_meter]
            }))
        self.logic.state = State(self.state_file_callback)
        self.logic.state.next_id = meter_id + 1
        electric_meter = self.deserialize_electric_meter(
            serialized_electric_meter)
        self.logic.state.electric_meters = {meter_id: electric_meter}
        self.logic.state.electric_meters_dict = {
            meter_id: serialized_electric_meter
        }

        self.db_file = FileLikeMock(value=json.dumps({
            'data_per_hour':
            self.data_per_hour,
            'keep_raw':
            self.keep_raw,
            'keep_day':
            self.keep_day,
            'keep_month':
            self.keep_month,
            'keep_year':
            self.keep_year,
            'keep_years':
            self.keep_years,
            'data_sources': [{
                'id': meter_id,
                'data': {
                    'day_counter': 0,
                    'month_counter': 0,
                    'year_counter': 0,
                    'years_counter': 0,
                    'day_sum': 0,
                    'month_sum': 0,
                    'year_sum': 0,
                    'years_sum': 0,
                    'raw_data': [],
                    'day_data': [],
                    'month_data': [],
                    'year_data': [],
                    'years_data': []
                }
            }]
        }))
        self.logic.database = Database.load_database(
            self.database_file_callback)

        # Test
        removed_meter = self.logic.remove_electric_meter(meter_id)

        # Assert
        self.assertEqual(removed_meter, serialized_electric_meter)
        self.assertNotIn(electric_meter, self.state.electric_meters.values())
        self.assertEqual(0, len(self.state.electric_meters_dict.values()))
        self.assertEqual({
            'next_id': 11,
            'electric_meters': []
        }, json.loads(self.state_file.getvalue()))
        self.assertEqual(0, len(self.database.datasources))
        self.assertEqual(
            {
                'data_per_hour': self.data_per_hour,
                'keep_raw': self.keep_raw,
                'keep_day': self.keep_day,
                'keep_month': self.keep_month,
                'keep_year': self.keep_year,
                'keep_years': self.keep_years,
                'data_sources': []
            }, json.loads(self.db_file.getvalue()))