테스트 강박 :: 2008/11/28 15:19

정보화용역사업 마무리즈음 백업서비스를 구현해야 할 일이 생겼다. 장고 데이터 덤프기능을 이용하여 데이터 백업하고, 사용자 업로드 파일디렉토리는 rdiff-backup을 쓰면 깔끔하겠다 생각하고는 쉘스크립트. (cron으로 하루 1회 백업하고, 30일 지난 것 지우기)

#!/bin/sh

rdiff-backup /usr/local/dsite /home/agrims_backup/file
rdiff-backup --remove-older-than 30D /home/agrims_backup/file
/usr/local/bin/python /usr/local/dsite/manage.py dumpdata > /home/agrims_backup/db/dump_`date '+%Y%m%d.json'` 2>&1

앗, 쉘스크립트로 dump_20081101.json 이렇게 되어 있는 파일의 30일 이전 것을 어떻게 찾지? 음냐; 저부분은 파이썬으로!

#!/usr/local/bin/python

import sys, os
from datetime import date, timedelta

target = sys.argv[1]
for d in os.listdir(target):
    if d.startswith('dump_'):
        year = int(d[5:9])
        month = int(d[9:11])
        day = int(d[11:13])
        file_date = date(year,month,day)
        if date.today() - file_date > timedelta(30):
            os.remove(os.path.join(target, d))

잘 돌아가나 확인을 해봐야하는데, 음... 대충 target 디렉토리에 빈 파일 넣어보고 돌려본다. 잘 되네... 그러고 넘어가려던 중 갑자기 어제 읽은 포스트가 생각났다.

저는 테스트 케이스가 없는 코드를 레거시 코드로 봅니다. ... 테스트 없는 코드는 나쁜 코드입니다. 얼마나 잘 짰는지는 중요치 않아요. 아무리 예뻐도, 아무리 객체지향적이어도, 아무리 캡슐화가 잘 되어 있어도 소용 없습니다. 테스트가 있으면 빠르고 검증가능한 방식으로 코드의 행위를 수정할 수 있습니다. 테스트가 없으면 코드가 좋아지고 있는지 나빠지고 있는지 알 방법이 없지요.

꿀꺽. 나도 테스트가 중요하다는 것쯤은 안다고. 음냐; 테스트 만들자. 그간 테스트없어서 고달펐던 몇몇 일들을 떠올리며,,, 아 그리고 며칠전 읽었던 파이썬스러운 용법들 적용해서 멋스럽게~

#!/usr/local/bin/python

import sys, os
from datetime import date, timedelta

def remove_older_than(target, days, basis=None):
    if not basis:
        basis = date.today()
    for d in os.listdir(target):
        if d.startswith('dump_'):
            year = int(d[5:9])
            month = int(d[9:11])
            day = int(d[11:13])
            file_date = date(year,month,day)
            if basis - file_date > timedelta(30):
                os.remove(os.path.join(target, d))

def main():
    target = sys.argv[1]
    days = sys.argv[2]
    remove_older_than(target, days)
    return 0

import unittest
class RemoveTest(unittest.TestCase):
    def test_remove_older_than(self):
        days = 30
        basis = date(2008, 11, 27)
        target = '/tmp'
        recent_filename = os.path.join(target, 'dump_20081101.json')
        old_filename = os.path.join(target, 'dump_20081001.json')
        open(os.path.join(target, 'dump_20081101.json'),'w')
        open(os.path.join(target, 'dump_20081001.json'),'w')
        self.assert_(os.path.exists(recent_filename))
        self.assert_(os.path.exists(old_filename))

        remove_older_than(target, days, basis)
        self.assert_(os.path.exists(recent_filename))
        self.assert_(not os.path.exists(old_filename))

    def tearDown(self):
        try:
            os.remove(os.path.join(target, 'dump_20081101.json'))
            os.remove(os.path.join(target, 'dump_20081001.json'))
        except:
            pass

if __name__=='__main__':
    unittest.main()
    status = main()
    sys.exit(status)

테스트가 생겨서 뿌듯하긴 하다. 하지만 뭔가 아쉬움이 남는 것이 배보다 배꼽인듯한...
Trackback Address :: http://yong27.biohackers.net/trackback/341
  • yong27의 느낌

    Tracked from yong27's me2DAY | 2009/02/05 20:25 | DEL

    manage.py dumpdata/loaddata를 이용한 django 백업 및 복구가 데이터가 많아지니까 버벅댄다. 이런 작업은 DB에서 제공하는 것을 써야하나 보다.

Name
Password
Homepage
Secret